为什么重新定义自己的功能在Chrome/IE和Firefox中表现不同?

Dag*_*bit 11 javascript

请考虑以下代码:

function f() {
    f = eval("" + f);
    console.log("Inside a call to f(), f is: \n%s", f);
}

f();

console.log("After a call to f(), f is: \n%s", f);
Run Code Online (Sandbox Code Playgroud)

我期望f在执行期间始终定义.但是,在Chrome和IE中,它是undefined在第一个console.log被调用时,而在Firefox中,它是undefined在第二个console.log被调用时.

为什么f不总是定义?为什么Chrome/IE和Firefox的行为有所不同?

http://jsfiddle.net/G2Q2g/

Firefox 26上的输出:

在调用f()时,f是:

function f() {
    f = eval("" + f);
    console.log("Inside a call to f(), f is: \n%s", f);
}
Run Code Online (Sandbox Code Playgroud)

在调用f()之后,f是:

undefined
Run Code Online (Sandbox Code Playgroud)

Chrome 31和IE 11上的输出:

在调用f()时,f是:

undefined
Run Code Online (Sandbox Code Playgroud)

在调用f()之后,f是:

function f() {
    f = eval("" + f);
    console.log("Inside a call to f(), f is: \n%s", f);
}
Run Code Online (Sandbox Code Playgroud)

Ben*_*aum 8

首先,让我们谈谈我们"期望"的内容.

我会天真地期待两种情况都会回归undefined.

  • 就像:eval("function foo(){}")返回undefined.

  • 就像我们有一个函数声明一样 - 它不返回函数值但是设置它.

  • 就像langue规范所说的严格模式一样.

更新:通过规范挖掘更多 - Firefox在这里是正确的.

这是Firefox正在做的事情

可视化:

  1. f = eval("" + f); //将左侧设置为f我们所在的函数
  2. f = eval(""+ f); // f在此函数的范围内声明一个新函数
  3. f = undefined; //自undefined === eval("function(){}");*

*因为函数声明不返回任何东西 - 就像函数foo(){}没有返回值一样

由于f是在步骤1中决定的,所以现在对我们所使用的函数的引用被undefined覆盖,并且声明的局部闭包f用相同的代码声明.现在我们这样做:

console.log("Inside a call to f(), f is: \n%s",f) // f是局部闭包变量,它最接近

突然间,很明显我们得到了函数 - 它是一个成员变量.

但是,一旦我们逃脱了这个功能

console.log("After a call to f(), f is: \n%s",F);

这里,f是未定义的,因为我们在步骤1中覆盖了它.

Chrome和IE错误地将其分配给错误f并在分配左侧之前评估右侧.

为什么它在严格模式下工作

请注意,下一节在输入eval代码中说明:

让strictVarEnv成为调用NewDeclarativeEnvironment传递LexicalEnvironment作为参数的结果.

这解释了为什么它在严格模式下工作 - 它都在新环境中运行.


同样的事情,但更多的文字和更少的图形

  • "查找" ff =(因为左侧必须进行评估,首先,这指的是本地的副本f,也就是说,首先评估左手边.
  • 执行eval返回的调用,undefined但声明一个新的本地函数f.
  • 由于ffrom f =在函数本身之前被评估,当我们为它分配undefined时,我们实际上正在替换全局函数
  • 因此,当我们在console.log内部时,我们指的是在eval中声明的本地副本,因为它在范围链中更接近.
  • 当我们在外面做console.log,我们现在指的f是我们指定未定义的'全局' .

诀窍是,我们分配的f和我们记录的f是两个不同的fs.这是因为总是首先评估赋值的左侧(规范中的第11.13.1节).

IE和Chrome错误地分配给本地f.由于规范明确告诉我们,这显然是不正确的:

  1. 让lref成为评估LeftHandSideExpression的结果.

  2. 让rref成为评估AssignmentExpression的结果.

因此,正如我们所看到的那样,首先需要评估lref.

(链接到相关的esdiscuss主题)