为什么未执行的eval会对某些浏览器中的行为产生影响? [英] Why does an unexecuted eval have an effect on behavior in some browsers?
问题描述
假设我有这两个函数:
function change(args) {
args[0] = "changed";
return " => ";
}
function f(x) {
return [x, change(f.arguments), x];
}
console.log(f("original"));
在大多数浏览器中,除Opera外,它返回 [original, =>,原创]
。
In most browsers, except Opera, this returns ["original", " => ", "original"]
.
但如果我更改 f
像这样的函数,
But if I change the f
function like this,
function f(x) {
return [x, change(f.arguments), x];
eval("");
}
它将返回 [original,= >,在IE9,Safari 5和Firefox 16和17中更改了]
。
如果我替换 eval()
带参数
,它也会在Chrome中更改。
If I replace eval("")
with arguments
, it will also change in Chrome.
您可以在jsFiddle上在自己的浏览器上测试它。
You can test it on your own browser on jsFiddle.
我根本不明白这种行为。如果函数在执行这些语句之前返回,那么这些语句如何影响返回值?即使语句被执行,为什么它们会对参数变异产生任何影响?
I don't understand this behavior at all. If the function returns before those statements are executed, how can those statements affect the return value? Even if the statements were executed, why would they have any effect on argument mutation?
推荐答案
TL ; DR
可能的原因是非标准
function.arguments
与功能代码的浏览器优化的交互包含eval
和/或参数
。但是,只有熟悉每个浏览器实现细节的人才能够深入解释为什么。
TL;DR
The likely cause is the interaction of the non-standard
function.arguments
with browser optimizations for function code containingeval
and/orarguments
. However, only people familiar with the implementation details on each browser would be able to explain the why in depth.
这里的主要问题似乎是使用非标准的 Function.prototype.arguments
。当你不使用它时,奇怪的行为就会消失。
The main problem here appears to be the use of the non-standard Function.prototype.arguments
. When you don't use it, the strange behavior goes away.
规范只提到 参数
对象,并且永远不会说它可能被视为属性,前缀为 [了funcName]。
。我不确定它来自哪里,但它可能是ES3之前的版本,为了向后兼容而保留在浏览器上。正如Cory的回答所述,使用的是现在不鼓励使用MDN 一>。但是, MSDN 什么都不反对。我还发现它在这个浏览器之间的兼容性规范中提到了< sup> * ,供应商似乎没有一致地实施(没有浏览器通过所有测试的)。此外,在严格模式下不允许使用参数
作为函数的属性(再次,这不在ECMA规范中,IE9似乎忽略了限制)。
The specification only mentions the arguments
object, and never says it might be treated as a property, prefixed with [funcName].
. I'm not sure where that came from, but it's probably something pre-ES3, kept on browsers for backward compatibility. As Cory's answer states, that use is now discouraged on MDN. MSDN, however, doesn't say anything against it. I also found it mentioned on this specification for compatibility between browsers*, which does not appear to be implemented consistently by vendors (no browser pass all tests). Also, using arguments
as a property of the function is not allowed in strict mode (again, that's not in the ECMA specs, and IE9 seems to ignore the restriction).
然后来 eval
和参数
。 如您所知,ECMAScript规范需要一些 extra 操作以便可以使用那些语言结构(在 eval
的情况下,操作因呼叫而异)是否直接。由于这些操作会对性能产生影响,(某些?)JavaScript引擎会执行优化以避免它们,如果 eval
或参数
没用过。这些优化与使用 Function
对象的非标准属性相结合,似乎是导致您获得奇怪结果的原因。不幸的是,我不知道每个浏览器的实现细节,所以我无法给出关于为什么我们看到这些附带效果的确切答案。
Then come eval
and arguments
. As you are aware, the ECMAScript specification requires some extra operations to be performed so those language constructs can be used (in the case of eval
, the operation is different depending on the call being direct or not). Since those operations can have an impact on performance, (some?) JavaScript engines perform optimizations to avoid them if eval
or arguments
are not used. Those optimizations, combined with the use of a non-standard property of the Function
object, seem to be what's causing the strange results you got. Unfortunately, I don't know the implementation details for each browser, so I can't give you a precise answer on why we see those collateral effects.
(*)由 SO用户<编写的规范< /顺便说道。
我跑了一些测试看看如何 eval
(直接和间接通话),参数
和 fn.arguments
在IE,Firefox和Chrome上进行交互。因为我们处理的是非标准的 fn.arguments
,所以每个浏览器的结果都不同也就不足为奇了。
I ran some tests to see how eval
(direct and indirect calls), arguments
and fn.arguments
interact on IE, Firefox and Chrome. It's not surprising that the results vary on each browser, since we're dealing with the non-standard fn.arguments
.
第一个测试只是检查 fn.arguments
和参数
的严格相等,如果存在 eval
以任何方式影响它。正如你在问题中所说的那样,我的Chrome测试不可避免地受到参数
的影响,这会对结果产生影响。结果如下:
The first test just checks for strict equality of fn.arguments
and arguments
, and if the presence of eval
affects that in any way. Inevitably, my Chrome tests are contamined by the presence of arguments
, which has an effect on the results, as you said in the question. Here are the results:
| 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上相同。但是,在Chrome中,如果功能代码不包含直接的 eval
调用,则它们只相等。
You can see IE and Firefox are more consistent: the objects are always equal on IE, and never equal on Firefox. In Chrome, however, they're only equal if the function code does not contain a direct eval
call.
剩下的测试是基于如下函数的赋值测试:
The remaining tests are assignment tests based on functions that look like the following:
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)("")
}
以下是每个经过测试的浏览器的结果。
Below are the results for each tested browser.
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是最一致的浏览器。如IE 参数=== fn.arguments
始终为true, x
,参数[ 0]
或 function.arguments [0]
都指向相同的值。如果您更改其中任何一个,则所有三个都将输出相同的更改值。
First of all, it seems my IE tests give different results than what is stated in the question; I always get "changed" on IE. Maybe we used different IE builds? Anyway, what the results above show is that IE is the most consistent browser. As on IE arguments === fn.arguments
is always true, x
, arguments[0]
or function.arguments[0]
all point to the same value. If you change any of them, all three will output the same changed value.
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不太一致:虽然参数
在Firefox上从不 === fn.arguments
, eval
对作业有影响。如果没有直接调用 eval
,更改 arguments [0]
会更改 x
也是,但不会改变 fn.arguments [0]
。更改 fn.arguments [0]
不会更改 x
或参数[0]
。令人惊讶的是,更改 fn.arguments [0]
本身并没有改变!
Firefox 16.0.2 is less consistent: although arguments
is never === fn.arguments
on Firefox, eval
has an effect on the assignments. Without a direct call to eval
, changing arguments[0]
changes x
too, but does not change fn.arguments[0]
. Changing fn.arguments[0]
does not change either x
or arguments[0]
. It was a complete surprise that changing fn.arguments[0]
does not change itself!
当<$ c时引入$ c> eval(),行为不同:更改 x
,参数[0]之一]
或 function.arguments [0]
开始影响其他两个。所以它就像参数
变成 === function.arguments
–除了它没有,Firefox仍然说 arguments === function.arguments
是 false
。当使用间接 eval
调用时,Firefox的行为就好像没有 eval
。
When eval("")
is introduced, the behavior is different: changing one of x
, arguments[0]
or function.arguments[0]
starts affecting the other two. So it's like arguments
becomes === function.arguments
– except that it does not, Firefox still says that arguments === function.arguments
is false
. When an indirect eval
call is used instead, Firefox behaves as if there were no eval
.
Chrome 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
调用,参数
和 fn.arguments $ c之间的链接$ c>似乎打破(这是有道理的,考虑到
参数=== fn.arguments
false
当 eval()
存在)。即使在分配后,Chrome也会出现 fn.arguments [0]
为原始
的奇怪情况,但它会在 eval()
存在(在Firefox上,当没有 eval
或间接调用时)。
Chrome's behavior is similar to Firefox's: when there is no eval
or an indirect eval
call, it behaves consistently. With the direct eval
call, the link between arguments
and fn.arguments
seem to break (which makes sense, considering arguments === fn.arguments
is false
when eval("")
is present). Chrome also presents the weird case of fn.arguments[0]
being original
even after assignment, but it happens when eval("")
is present (while on Firefox it happens when there's no eval
, or with the indirect call).
如果有人想要运行它们,这是测试的完整代码。还有一个 jsfiddle的实时版本。
Here is the full code for the tests, if anyone wants to run them. There's also a live version on 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');
这篇关于为什么未执行的eval会对某些浏览器中的行为产生影响?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!