if (function f() {}) {
console.log(f) // Throw an error: f is not defined
}
为什么日志会导致错误,f
已经在上面的表达式中定义了?
您希望这相当于:
function f () {}
if (true) {
console.log(f); // Throw an error: f is not defined
}
答案 0 :(得分:14)
当你说
时function f () {}
这是一个函数声明语句。此功能将在封闭环境中定义。因此,如果它是在另一个函数中定义的,那么该函数将在该环境中定义,您可以通过名称访问该函数。
但是,当你在表达式中使用函数声明时,它不会被视为函数声明,而是函数表达式,它将被这样评估(从ECMA Script 5.1 Standard Specification引用)
13 Function Definition: Semantics
制作
FunctionExpression:
function Identifier ( FormalParameterListopt ) { FunctionBody }
评估如下:
- 让 funcEnv成为调用NewDeclarativeEnvironment 传递正在运行的执行上下文Lexical Environment作为参数的结果
- 让envRec成为funcEnv的环境记录。
- 调用envRec的 CreateImmutableBinding(N)具体方法,将Stringier的String值作为参数。
- 让闭包是创建一个新的Function对象的结果,该对象在13.2中指定,其中参数由FormalParameterListopt指定,而body由FunctionBody指定。传入funcEnv作为范围。如果FunctionExpression包含在strict code中或者其FunctionBody是严格代码,则将
true
作为Strict标记传递。- 调用envRec的 InitializeImmutableBinding(N,V)具体方法,将Identifier的String值和闭包作为参数传递。
- 返回封闭。
醇>
因此,当您在表达式中创建函数时,
将创建一个新的环境上下文(参见第一项)
函数的名称将绑定到新创建的环境(参见第三项)。
函数体将用于创建实际的函数对象(参见第四项)
创建的实际函数对象绑定到新创建的上下文中的函数名称(参见第五项)
然后返回函数对象。
由程序将函数对象分配给当前环境上下文中的变量以保留该函数。否则,当表达式评估完成时,新创建的环境上下文变为无效。因此,函数f
将无法在外部显示。
答案 1 :(得分:7)
您的假设不正确。这两个片段并不相同。第一个是named function expression,您不存储值(实际的函数对象)。这样的对象总是真实的 - 因此if
得到了执行。
这些函数的函数名是(或者应该)只在函数本身 中可见(除了一些早期的IE版本)。因此,由于f
根本不存在,因此当您尝试将ReferenceError
传递给console.log
时会引发{{1}}。
答案 2 :(得分:4)
函数声明语句在其周围范围内定义函数,甚至将定义移到顶部。但是,这是一个命名的函数表达式。该函数由其内部的名称定义,但不在外部定义(旧的IE除外,它是一个错误)。除此之外,它只是一个设置了名称的函数。仅当语句以关键字function
开头时,它才算作函数语句。
这有效:
function f(){};
if (f) console.log(f);
答案 3 :(得分:3)
if (function f() {}) {
console.log(f) // Throw an error: f is not defined
}
函数声明被视为函数表达式(如function(){}),而不是函数语句。
证明:
if (function f(x) {return x}(1)) {
console.log('foo'); //Will log
}
if (function f(x) {return x}(false)) {
console.log('foo'); //Will not log
}
如果你的函数只是一个语句,你就不能立即执行它(不用括号括起来,或者用!
作为前缀,把它变成表达式)。
function f(x) {return x}(false) //Would yield a syntax error