请考虑以下代码:
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);
}
首先,让我们谈谈我们所期望的。
我会 天真 期待两个案件都返回 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
并在作业左侧评估右侧。
为什么它在严格模式下工作
请注意,输入eval代码中的下一部分说明:
让strictVarEnv成为调用NewDeclarativeEnvironment传递LexicalEnvironment作为参数的结果。
这解释了为什么它在严格模式下工作 - 它都在新环境中运行。
同样的事情,但更多的文字和更少的图形
- “找”
f
从 f =
(因为必须评估左侧 第一。这指的是 本地 备份 f
。也就是说,首先评估左侧。
- 执行
eval
回电的电话 undefined
但声明了一个新的本地函数 f
。
- 以来
f
从 f =
在函数本身之前进行了评估,当我们为它分配undefined时,我们实际上正在替换全局函数
- 所以,当我们这样做
console.log
我们指的是在eval中声明的本地副本,因为它在范围链中更接近。
- 当我们在外面做的时候
console.log
,我们现在指的是'全球' f
我们分配了未定义的。
诀窍是,我们分配的f和我们记录的f是两个不同的fs。这是因为总是首先评估赋值的左侧(规范中的第11.13.1节)。
IE和Chrome犯了分配给本地的错误 f
。由于规范明确告诉我们,这显然是不正确的:
让lref成为评估LeftHandSideExpression的结果。
让rref成为评估AssignmentExpression的结果。
因此,正如我们所看到的那样,首先需要评估lref。
(链接到相关的esdiscuss线程)
对不起,我现在只回答你的第一个问题: - /
我希望在执行期间始终定义f。为什么f不总是被定义?
两件事情:
eval
函数声明确实返回 undefined
。它可以在评估时重新定义,但您可以分配 undefined
至 f
其后。
f
是函数中的局部变量 f
,因为命名函数可以在自己的范围内使用。
检查此行为 http://jsfiddle.net/G2Q2g/5/。
所以现在你至少可以问一下
为什么 undefined
当第二个 console.log
在Firefox中调用,而不是Opera,Chrome和IE中的正确行为?
将您的代码更改为
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返回最后一个表达式的值。那就是你在这里看到的。