请考虑以下代码:
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表现不同?
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); }
答案 0 :(得分:8)
首先,让我们谈谈我们期望的内容。
我会天真地期望两个案例都返回undefined
。
就像:eval("function foo(){}")
返回undefined。
就像我们有一个函数声明一样 - 它不返回函数值但是设置它。
更新:在通过规范挖掘更多内容后, 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
,并在作业的左侧评估右侧。
请注意,下一部分在输入评估代码中说明:
让strictVarEnv成为调用NewDeclarativeEnvironment传递LexicalEnvironment作为参数的结果。
这解释了为什么它在严格模式下运行 - 它们都在新环境中运行。
同样的事情,但是更多的文字和更少的图形
f
“查找”f =
(因为左侧必须首先评估 。这是指{em> local { {1}}。也就是说,首先评估左侧。f
的{{1}}调用,但声明eval
的新本地函数。 undefined
的{{1}}在函数本身之前被评估,当我们为其分配undefined时,我们实际上正在替换全局函数f
内部时,我们指的是在eval中声明的本地副本,因为它在范围链中更接近。f
时,我们现在指的是我们指定为未定义的'全球'f =
。诀窍是,我们分配的f和我们记录的f是两个不同的fs。这是因为总是首先评估赋值的左侧(规范中的第11.13.1节)。
IE和Chrome错误地指定了本地console.log
。由于规范明确告诉我们,这显然是不正确的:
让lref成为评估LeftHandSideExpression的结果。
- 醇>
让rref成为评估AssignmentExpression的结果。
因此,正如我们所看到的那样,首先需要评估lref。
答案 1 :(得分:2)
对不起,我现在只回答你的第一个问题: - /
我希望在执行期间始终定义f。为什么f不总是被定义?
两件事:
eval
函数声明确实返回undefined
。可以在评估时对其进行重新定义,然后再将undefined
分配给f
。f
是函数f
中的局部变量,因为命名函数在它们自己的作用域中可用。在http://jsfiddle.net/G2Q2g/5/。
中检查此行为所以现在你至少可以问一下
为什么在Firefox中调用第二个
undefined
时console.log
,而不是Opera,Chrome和IE中的正确行为?
答案 2 :(得分:0)
将您的代码更改为
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);
它会起作用。请注意,函数源被包装在“(”“)”括号中,使其成为左值表达式,而不是声明。
这是http://jsfiddle.net/G2Q2g/8/
更新:
eval(str)
解析并执行字符串作为程序(ECMAScript术语)。
函数声明本身就是一个声明,它没有任何价值。但这( func() ... )
是一个有价值的表达。并且eval返回最后一个表达式的值。这就是你在这里看到的。