请考虑以下代码:
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);
我期望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); }在调用f()之后,f是:
undefined
Chrome 31和IE 11上的输出:
在调用f()时,f是:
undefined在调用f()之后,f是:
function f() { f = eval("" + f); console.log("Inside a call to f(), f is: \n%s", f); }
Benjamin Gru.. 8
首先,让我们谈谈我们"期望"的内容.
我会天真地期待两种情况都会回归undefined
.
就像:eval("function foo(){}")
返回undefined.
就像我们有一个函数声明一样 - 它不返回函数值但是设置它.
就像langue规范所说的严格模式一样.
更新:通过规范挖掘更多 - Firefox在这里是正确的.
这是Firefox正在做的事情
可视化:
f
= eval("" + f);
//将左侧设置为f
我们所在的函数
f =
eval(""+ f); //f
在此函数的范围内声明一个新函数
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作为参数的结果.
这解释了为什么它在严格模式下工作 - 它都在新环境中运行.
同样的事情,但更多的文字和更少的图形
"查找" f
从f =
(因为左侧必须进行评估,首先,这指的是本地的副本f
,也就是说,首先评估左手边.
执行eval
返回的调用,undefined
但声明一个新的本地函数f
.
由于f
from f =
在函数本身之前被评估,当我们为它分配undefined时,我们实际上正在替换全局函数
因此,当我们在console.log
内部时,我们指的是在eval中声明的本地副本,因为它在范围链中更接近.
当我们在外面做console.log
,我们现在指的f
是我们指定未定义的'全局' .
诀窍是,我们分配的f和我们记录的f是两个不同的fs.这是因为总是首先评估赋值的左侧(规范中的第11.13.1节).
IE和Chrome错误地分配给本地f
.由于规范明确告诉我们,这显然是不正确的:
让lref成为评估LeftHandSideExpression的结果.
让rref成为评估AssignmentExpression的结果.
因此,正如我们所看到的那样,首先需要评估lref.
(链接到相关的esdiscuss主题)
首先,让我们谈谈我们"期望"的内容.
我会天真地期待两种情况都会回归undefined
.
就像:eval("function foo(){}")
返回undefined.
就像我们有一个函数声明一样 - 它不返回函数值但是设置它.
就像langue规范所说的严格模式一样.
更新:通过规范挖掘更多 - Firefox在这里是正确的.
这是Firefox正在做的事情
可视化:
f
= eval("" + f);
//将左侧设置为f
我们所在的函数
f =
eval(""+ f); //f
在此函数的范围内声明一个新函数
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作为参数的结果.
这解释了为什么它在严格模式下工作 - 它都在新环境中运行.
同样的事情,但更多的文字和更少的图形
"查找" f
从f =
(因为左侧必须进行评估,首先,这指的是本地的副本f
,也就是说,首先评估左手边.
执行eval
返回的调用,undefined
但声明一个新的本地函数f
.
由于f
from f =
在函数本身之前被评估,当我们为它分配undefined时,我们实际上正在替换全局函数
因此,当我们在console.log
内部时,我们指的是在eval中声明的本地副本,因为它在范围链中更接近.
当我们在外面做console.log
,我们现在指的f
是我们指定未定义的'全局' .
诀窍是,我们分配的f和我们记录的f是两个不同的fs.这是因为总是首先评估赋值的左侧(规范中的第11.13.1节).
IE和Chrome错误地分配给本地f
.由于规范明确告诉我们,这显然是不正确的:
让lref成为评估LeftHandSideExpression的结果.
让rref成为评估AssignmentExpression的结果.
因此,正如我们所看到的那样,首先需要评估lref.
(链接到相关的esdiscuss主题)