TL;DR
考えられる原因は、および/またはを含む関数コードの非標準function.arguments
とブラウザの最適化の相互作用です。ただし、各ブラウザーの実装の詳細に詳しい人だけが、その理由を詳しく説明できます。eval
arguments
ここでの主な問題は、非標準の使用にあるようFunction.prototype.arguments
です。使用しないと、奇妙な動作はなくなります。
arguments
仕様ではobjectについてのみ言及しており、接頭辞が[funcName].
. それがどこから来たのかはわかりませんが、おそらく ES3 以前のもので、下位互換性のためにブラウザーに保持されていました。Cory's answer が述べているように、その使用は現在 MDN では推奨されていません。ただし、 MSDNはそれに対して何も述べていません。また、ブラウザ間の互換性についてこの仕様で言及されていることもわかりました*。これは、ベンダーによって一貫して実装されているようには見えません (すべてのテストに合格するブラウザはありません)。また、arguments
関数のプロパティとして使用することは、厳密モードでは許可されていません (繰り返しますが、これは ECMA 仕様にはなく、IE9 は制限を無視しているようです)。
それから来eval
てarguments
。ご存知のように、ECMAScript 仕様では、これらの言語構造を使用できるように、いくつかの追加 操作を実行する必要があります (の場合、呼び出しが直接eval
かどうかによって操作が異なります)。これらの操作はパフォーマンスに影響を与える可能性があるため、(一部?) JavaScript エンジンは最適化を実行して、使用されていない場合はそれらを回避します。これらの最適化は、オブジェクトの非標準プロパティの使用と組み合わされて、得られた奇妙な結果を引き起こしているようです。残念ながら、各ブラウザの実装の詳細がわからないため、その理由について正確な回答はできません。eval
arguments
Function
これらの副次的効果が見られます。
(*)ちなみにSO ユーザーが書いた仕様です。
テスト
eval
方法(直接呼び出しと間接呼び出し)を確認し、IE、Firefox、および Chrome で対話するarguments
ためのいくつかのテストを実行しました。fn.arguments
非標準のfn.arguments
.
fn.arguments
最初のテストでは、 と が厳密に等しいかどうか、およびarguments
の存在がeval
何らかの形で影響するかどうかをチェックするだけです。必然的に、あなたが質問で言ったように、私の Chrome テストは の存在によって汚染されarguments
、結果に影響を与えます。結果は次のとおりです。
| no eval | direct eval call | indirect eval call
-----------------------+-----------+--------------------+---------------------
IE 9.0.8112.16421 | true | true | true
FF 16.0.2 | false | false | false
Chrome 22.0.1229.94 | true | false | true
IE と Firefox のほうが一貫性があることがわかります。オブジェクトは IE では常に等しく、Firefox では決して等しくありません。eval
ただし、Chrome では、関数コードに直接呼び出しが含まれていない場合にのみ同等です。
残りのテストは、次のような関数に基づく割り当てテストです。
function fn(x) {
// Assignment to x, arguments[0] or fn.arguments[0]
console.log(x, arguments[0], fn.arguments[0]);
return; // make sure eval is not actually called
// No eval, eval(""), or (1,eval)("")
}
以下は、テストされた各ブラウザの結果です。
Internet Explorer 9.0.8112.16421
| no eval | direct eval call | indirect eval call
-----------------------------+---------------------------+---------------------------+--------------------------
arguments[0] = 'changed'; | changed, changed, changed | changed, changed, changed | changed, changed, changed
x = 'changed'; | changed, changed, changed | changed, changed, changed | changed, changed, changed
fn.arguments[0] = 'changed'; | changed, changed, changed | changed, changed, changed | changed, changed, changed
まず第一に、私の IE テストでは、質問に記載されている結果とは異なる結果が得られたようです。私は常にIEで「変更」されます。異なる IE ビルドを使用したのでしょうか。とにかく、上記の結果が示しているのは、IE が最も一貫性のあるブラウザーであることです。IEarguments === fn.arguments
と同じように、常に true 、x
、arguments[0]
またはfunction.arguments[0]
すべてが同じ値を指します。それらのいずれかを変更すると、3 つすべてが同じ変更された値を出力します。
Firefox 16.0.2
| no eval | direct eval call | indirect eval call
-----------------------------+------------------------------+---------------------------+-----------------------------
arguments[0] = 'changed'; | changed, changed, original | changed, changed, changed | changed, changed, original
x = 'changed'; | changed, changed, original | changed, changed, changed | changed, changed, original
fn.arguments[0] = 'changed'; | original, original, original | changed, changed, changed | original, original, original
Firefox 16.0.2 はあまり一貫性がarguments
ありません === fn.arguments
: Firefox にはありませんeval
が、割り当てに影響があります。を直接呼び出さないとeval
、変更arguments[0]
も変更されますが、変更されx
ませんfn.arguments[0]
。変更しても も も変更fn.arguments[0]
されませx
んarguments[0]
。fn.arguments[0]
変えても変わらないというのは全くの驚きでした!
がeval("")
導入されると、動作が異なります。 の 1 つを変更するか、x
他の 2 つに影響を与え始めます。そうでないことを除いて、Firefox はまだそれがであると言います。代わりに間接呼び出しを使用すると、Firefox は が存在しないかのように動作します。arguments[0]
function.arguments[0]
arguments
=== function.arguments
arguments === function.arguments
false
eval
eval
クロム 22.0.1229.94
| no eval | direct eval call | indirect eval call
-----------------------------+----------------------------+------------------------------+--------------------------
arguments[0] = 'changed'; | changed, changed, changed | changed, changed, original | changed, changed, changed
x = 'changed'; | changed, changed, changed | changed, changed, original | changed, changed, changed
fn.arguments[0] = 'changed'; | changed, changed, changed | original, original, original | changed, changed, changed
Chrome の動作は Firefox の動作と似ています。つまり、呼び出しがないeval
場合、または間接eval
呼び出しがある場合、一貫して動作します。直接eval
呼び出しでは、 と の間のリンクがarguments
壊れているように見えます (これは、がいつ存在fn.arguments
するかを考えると理にかなっています)。Chromeでは、代入後も存在するという奇妙なケースが示されますが、 が存在する場合に発生します (Firefox では、 がない場合、または間接呼び出しがある場合に発生します)。arguments === fn.arguments
false
eval("")
fn.arguments[0]
original
eval("")
eval
誰かがそれらを実行したい場合は、テストの完全なコードを次に示します。jsfiddle にもライブ バージョンがあります。
function t1(x) {
console.log("no eval: ", arguments === t1.arguments);
}
function t2(x) {
console.log("direct eval call: ", arguments === t2.arguments);
return;
eval("");
}
function t3(x) {
console.log("indirect eval call: ", arguments === t3.arguments);
return;
(1, eval)("");
}
// ------------
function t4(x) {
arguments[0] = 'changed';
console.log(x, arguments[0], t4.arguments[0]);
}
function t5(x) {
x = 'changed';
console.log(x, arguments[0], t5.arguments[0]);
}
function t6(x) {
t6.arguments[0] = 'changed';
console.log(x, arguments[0], t6.arguments[0]);
}
// ------------
function t7(x) {
arguments[0] = 'changed';
console.log(x, arguments[0], t7.arguments[0]);
return;
eval("");
}
function t8(x) {
x = 'changed';
console.log(x, arguments[0], t8.arguments[0]);
return;
eval("");
}
function t9(x) {
t9.arguments[0] = 'changed';
console.log(x, arguments[0], t9.arguments[0]);
return;
eval("");
}
// ------------
function t10(x) {
arguments[0] = 'changed';
console.log(x, arguments[0], t10.arguments[0]);
return;
(1, eval)("");
}
function t11(x) {
x = 'changed';
console.log(x, arguments[0], t11.arguments[0]);
return;
(1, eval)("");
}
function t12(x) {
t12.arguments[0] = 'changed';
console.log(x, arguments[0], t12.arguments[0]);
return;
(1, eval)("");
}
// ------------
console.log("--------------");
console.log("Equality tests");
console.log("--------------");
t1('original');
t2('original');
t3('original');
console.log("----------------");
console.log("Assignment tests");
console.log("----------------");
console.log('no eval');
t4('original');
t5('original');
t6('original');
console.log('direct call to eval');
t7('original');
t8('original');
t9('original');
console.log('indirect call to eval');
t10('original');
t11('original');
t12('original');