为什么匿名函数表达式和命名函数表达式的初始化方式如此不同?

con*_*att 5 javascript function anonymous-function ecma262 ecmascript-5

我正在查看第13节或ECMAScript规范(第5节).匿名函数表达式初始化如下:

返回使用FormalParameterListopt指定的参数和FunctionBody指定的主体创建13.2中指定的新Function对象的结果.将正在运行的执行上下文的LexicalEnvironment作为Scope传递.如果FunctionExpression包含在严格代码中或者其FunctionBody是严格代码,则传入true作为Strict标志.

这个逻辑与初始化函数声明的方式非常相似.但是,请注意命名的funciton表达式的不同初始化方式.

  1. 让funcEnv成为调用NewDeclarativeEnvironment传递正在运行的执行上下文的Lexical Environment作为参数的结果
  2. 让envRec成为funcEnv的环境记录.
  3. 调用envRec的CreateImmutableBinding具体方法,将Identifier的String值作为参数传递.
  4. 让闭包是创建一个新的Function对象的结果,该对象在13.2中指定,其中参数由FormalParameterListopt和FunctionBody指定的body指定.传入funcEnv作为范围.如果FunctionExpression包含在严格代码中或者其FunctionBody是严格代码,则传入true作为Strict标志.
  5. 调用envRec的InitializeImmutableBinding具体方法,将Identifier和closure的String值作为参数传递.
  6. 关闭.

我知道命名/匿名函数表达式之间的一个重大区别是命名函数表达式可以在函数内递归调用,但这就是我能想到的.为什么设置如此不同?为什么需要执行这些额外的步骤?

kan*_*gax 9

所有"跳舞"的原因很简单.

命名函数表达式的标识符需要在函数范围内可用,但不能在外部使用.

typeof f; // undefined

(function f() {
  typeof f; // function
})();
Run Code Online (Sandbox Code Playgroud)

你如何f在功能内提供?

您无法在外部词汇环境中创建绑定,因为f不应在外部提供.并且您无法在内部变量环境中创建绑定,因为......它尚未创建; 该函数在实例化时尚未执行,因此从未发生过具有NewDeclarativeEnvironment的10.4.3(输入函数代码)步骤.

所以这样做的方法是创建一个中间词法环境,它直接从当前词汇 "继承",然后作为[[Scope]]传递给新创建的函数.

如果我们将13中的步骤分解为伪代码,您可以清楚地看到这一点:

// create new binding layer
funcEnv = NewDeclarativeEnvironment(current Lexical Environment)

envRec = funcEnv
// give it function's identifier
envRec.CreateImmutableBinding(Identifier)

// create function with this intermediate binding layer
closure = CreateNewFunction(funcEnv)

// assign newly created function to an identifier within this intermediate binding layer
envRec.InitializeImmutableBinding(Identifier, closure)
Run Code Online (Sandbox Code Playgroud)

所以内部的词法环境f(例如,在解析标识符时)现在看起来像这样:

(function f(){

  [global environment] <- [f: function(){}] <- [Current Variable Environment]

})();
Run Code Online (Sandbox Code Playgroud)

使用匿名函数,它看起来像这样:

(function() {

  [global environment] <- [Current Variable Environment]

})();
Run Code Online (Sandbox Code Playgroud)

  • 还有其他细微之处.函数表达式名称绑定是只读的,但仍允许在函数表达式的主体内使用相同的名称声明var或函数.描述这种语义(记住这只是一个规范)需要使用额外的环境记录. (2认同)