evalの返り値がよくわからない。



0   名前: NullPo : 2007/06/02(土) 22:46  ID:Z0FHbpib sub-ii
evalを使ってテストしてみました。
  var jsonText = '{ "name": "noname", "age": 19, "datas": [0, 1, 2, 3, 4] }';
//  var json = eval(jsonText); // ×
  var json = eval("(" + jsonText + ")"); // ○
  alert(json.name + "/" + json.age + "/" + json.datas[2]);

  var functionText = "function(){alert(1)}";
  var func = eval(functionText); // ×
  var func = eval("(" + functionText + ")"); // ×
  func();
それぞれ、上のコードがだめなのはなんとなくわかるのですが、
下がどちらもだめなのはなぜなのでしょうか。

1   名前: 匿名 : 2007/06/02(土) 22:46  ID:VdNduhWX sub-Cz
1、2 番目の理由は簡単。
{ "name": "noname", "age": 19, "datas": [0, 1, 2, 3, 4] }
// ステートメント冒頭の「{」がブロック開始と見なされ、続く「:」はラベルと見なされる
// → SyntaxError: invalid label

3、4 番目は少しややこしい。まず、以下のルールを ECMA 262-3 から引っ張ってくる。

・ステートメントにも(仮想的な)戻り値がある(§8.9)。
・関数宣言は戻り値として empty を返す(§14)。
・eval() は、評価結果が中途終了ではなく、かつ値が empty なら undefined を、それ以外ならその値を返す(§15.1.2.1)。

これをベースに、eval() の戻り値を確認する。
eval ('function test() { alert(1); }');
// IE:undefined, Fx:undefined, Op:undefined, Sa:undefined

これは明らかに関数宣言であり、全ブラウザの挙動は上記ルールに合致する。
eval('function() { alert(1); }');
// IE:undefined, Fx:Function, Op:SyntaxError, Sa:Function

関数に名前がないことから関数宣言ではないことは明らか。しかし、ステートメント冒頭の式は function で始まってはならないというルールもある(§12.4)ので関数式でもない。曖昧。
eval('(function() { alert(1); })');
// IE:undefined, Fx:Function, Op:Function, Sa:Function

これは明らかに関数式なので、Function オブジェクトを返さねばならない。

と言うわけで、IE(JScript)の挙動はよく分からない。IE は関数とスコープの扱いが微妙なので、Function() を併用することになると思うが、スコープがグローバルになるのがこれまた微妙。

2   名前: NullPo : 2007/06/02(土) 22:46  ID:VyJTFwOo sub-.G
>>1
詳細なご説明ありがとうございます。
しかしやはり (function(){alert(1);}) の記述は関数式を返すはず、なんですよね。

とりあえずFireFoxとOperaでテストしてみると、想定どおりの結果が返ってきました。
IEのバグ・・・?

3   名前: 匿名 : 2007/06/02(土) 22:46  ID:VdNduhWX sub-Cz
バグかどうかは MS に問い合わせないと分からない。たぶん JScript の仕様ということになるんじゃないかな。

実際問題、
eval('var func = ' + functionText);
func();
と書けば特に問題ないわけだし。



一応、ECMA 262-3 を流し読みしてみた。間違いがあるかもしれない。あと、ブラウザごとの動作確認はしてないです。

空文
・undefined を返す。

プロック文
・空なら undefined を返す。
・空でなければ、評価値が undefined ではない最後の文の評価値を返す。

式文
・式の評価値を返す。

var、continue、break 文
・undefined を返す。

with 文、ラベル文
・文の評価値を返す。

if 文
・実行した方の文の評価値を返す。条件式が false で else がなければ undefined を返す。

switch 文
・実行された case 文の評価値を返す。

for、while 文
・評価値が undefined ではない最後の文の評価値を返す。

return 文
・return; なら undefined を、return Expression; なら Expression の評価値を返す。

throw 文
・throw Expression; で Expression の評価値を返す。

4   名前: NullPo : 2007/06/02(土) 22:46  ID:VyJTFwOo sub-.G
>>3
なるほど〜。

(function(){alert(1);})
んー、この場合、式文ということになるんじゃないのですかね?
評価された結果がfunctionオブジェクト・・・と認識してました。

ま、確かにJScriptは独自実装ですから、認める必要のないところのバグは仕様になるのでしょうね。

とりあえずeval関数は過信しないほうがよさそうですね。
ありがとうございました。

一覧へ戻る