为什么未执行的eval会对某些浏览器中的行为产生影响? [英] Why does an unexecuted eval have an effect on behavior in some browsers?

查看:106
本文介绍了为什么未执行的eval会对某些浏览器中的行为产生影响?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有这两个函数:

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 containing eval and/or arguments. 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 似乎打破(这是有道理的,考虑到参数=== 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屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆