JavaScript中存储的命名函数表达式中标识符的不可变绑定记录在哪里?
Where is the immutable binding record of the identifier in a named function expression stored in JavaScript?
最近我 运行 了解了一些关于命名函数表达式 (NFE) 的有趣事实。我理解了一个NFE的函数名是可以在函数体内访问的,这样递归更方便,也为我们节省了arguments.callee
。并且函数名在函数体之外是不可用的。例如,
var foo = function bar() {
console.log(typeof bar);
};
typeof foo; // 'function'
typeof bar; // 'undefined', inaccessible outside the NFE
foo(); // 'function', accessible inside the NFE
这是一个有据可查的功能,kangax 有一篇关于 NFE 的精彩 post 并在那里提到了这种现象。最让我惊讶的是,NFE 的函数名不能与函数体中的其他值重新关联。例如,
(function foo() {
foo = 5;
alert(foo);
})(); // will alert function code instead of 5
在上面的例子中,我们试图用另一个值 5
重新绑定标识符 foo
。但这失败了!我转向 ES5 规范,发现在创建 NFE 时,会创建一个不可变的绑定记录并将其添加到词法环境的环境记录中。
问题是,当 NFE 在函数体内引用它自己的函数名称时,该名称被解析为 自由变量 。在上面的例子中,foo
在 NFE 内部被引用,但它既不是这个函数的形参也不是局部变量。所以它是一个自由变量,它的绑定记录可以通过NFE的[[scope]] 属性来解析。
所以考虑一下,如果我们在外部作用域中有另一个同名的标识符,似乎会有一些冲突。例如,
var foo = 1;
(function foo() {
alert(foo);
})(); // will alert function code rather than 1
alert(foo); // 1
当我们执行 NFE 时,自由变量 foo
被解析为它关联的函数。但是当控件退出 NFE 上下文时,foo
被解析为外部作用域中的局部变量。
所以我的问题如下:
- 函数名的不可变绑定记录存储在哪里?
- 在 NFE 内部解析时,为什么函数名称
foo
比 var foo = 1
重?它们的绑定记录是否存储在相同的词法环境中?如果是这样,如何?
- 函数名
foo
内部可访问外部不可见的现象是什么原因?
有人可以用 ES5 规范阐明这一点吗?我在网上没有找到太多讨论。
Where is the immutable binding record of the function name stored?
在您看不到的额外词法环境记录中:-)
How come the function name foo
outweigh var foo = 1
when resolved inside NFE?
事实上并非如此。您可以在函数范围内 声明 一个新的本地 var foo
而不会发生任何冲突,但如果您不这样做,那么自由 foo
变量会解析为不可变绑定.但是,它确实超过了范围链中较高的全局 foo
变量。
var foo = 1;
(function foo() { "use strict";
var foo = 2;
console.log(foo); // 2
}());
(function foo() { "use strict";
console.log(foo); // function …
foo = 2; // Error: Invalid assignment in strict mode
}());
Are their binding records stored in the same lexical environment?
没有。每个命名的函数表达式都包含在一个额外的词法环境中,该环境具有一个单一的、不可变的函数名称绑定,用函数初始化。
规范的 Function Definition (§13) 部分对此进行了描述。函数声明和匿名函数表达式的步骤基本上是"create a new function object with that function body using the current execution context's lexical environment for the Scope",而命名函数表达式则更复杂:
- 设
funcEnv
为调用NewDeclarativeEnvironment
passing the running execution context’s Lexical Environment作为参数的结果
- 设
envRec
为funcEnv
的环境记录。
- 调用
envRec
的CreateImmutableBinding(N)
具体方法,传递函数的Identifier
作为参数。
- 让
closure
成为创建新 Function 对象 […] 的结果。 将 funcEnv
作为 Scope 传入。
- 调用
envRec
的InitializeImmutableBinding(N,V)
具体方法,传递函数的Identifier
和closure
作为参数。
- Return
closure
.
它确实为函数表达式构造了一个额外的包装器环境。在具有块作用域的 ES6 代码中:
var x = function foo(){};
// is equivalent to
var x;
{
const foo = function() {};
x = foo;
}
// foo is not in scope here
What's behind the phenomenon that function name foo
is accessible inside but invisible outside?
foo
不可变绑定不是在当前执行上下文的词法环境中创建的,而是在仅用于函数表达式周围的闭包的包装器环境中创建的。
最近我 运行 了解了一些关于命名函数表达式 (NFE) 的有趣事实。我理解了一个NFE的函数名是可以在函数体内访问的,这样递归更方便,也为我们节省了arguments.callee
。并且函数名在函数体之外是不可用的。例如,
var foo = function bar() {
console.log(typeof bar);
};
typeof foo; // 'function'
typeof bar; // 'undefined', inaccessible outside the NFE
foo(); // 'function', accessible inside the NFE
这是一个有据可查的功能,kangax 有一篇关于 NFE 的精彩 post 并在那里提到了这种现象。最让我惊讶的是,NFE 的函数名不能与函数体中的其他值重新关联。例如,
(function foo() {
foo = 5;
alert(foo);
})(); // will alert function code instead of 5
在上面的例子中,我们试图用另一个值 5
重新绑定标识符 foo
。但这失败了!我转向 ES5 规范,发现在创建 NFE 时,会创建一个不可变的绑定记录并将其添加到词法环境的环境记录中。
问题是,当 NFE 在函数体内引用它自己的函数名称时,该名称被解析为 自由变量 。在上面的例子中,foo
在 NFE 内部被引用,但它既不是这个函数的形参也不是局部变量。所以它是一个自由变量,它的绑定记录可以通过NFE的[[scope]] 属性来解析。
所以考虑一下,如果我们在外部作用域中有另一个同名的标识符,似乎会有一些冲突。例如,
var foo = 1;
(function foo() {
alert(foo);
})(); // will alert function code rather than 1
alert(foo); // 1
当我们执行 NFE 时,自由变量 foo
被解析为它关联的函数。但是当控件退出 NFE 上下文时,foo
被解析为外部作用域中的局部变量。
所以我的问题如下:
- 函数名的不可变绑定记录存储在哪里?
- 在 NFE 内部解析时,为什么函数名称
foo
比var foo = 1
重?它们的绑定记录是否存储在相同的词法环境中?如果是这样,如何? - 函数名
foo
内部可访问外部不可见的现象是什么原因?
有人可以用 ES5 规范阐明这一点吗?我在网上没有找到太多讨论。
Where is the immutable binding record of the function name stored?
在您看不到的额外词法环境记录中:-)
How come the function name
foo
outweighvar foo = 1
when resolved inside NFE?
事实上并非如此。您可以在函数范围内 声明 一个新的本地 var foo
而不会发生任何冲突,但如果您不这样做,那么自由 foo
变量会解析为不可变绑定.但是,它确实超过了范围链中较高的全局 foo
变量。
var foo = 1;
(function foo() { "use strict";
var foo = 2;
console.log(foo); // 2
}());
(function foo() { "use strict";
console.log(foo); // function …
foo = 2; // Error: Invalid assignment in strict mode
}());
Are their binding records stored in the same lexical environment?
没有。每个命名的函数表达式都包含在一个额外的词法环境中,该环境具有一个单一的、不可变的函数名称绑定,用函数初始化。
规范的 Function Definition (§13) 部分对此进行了描述。函数声明和匿名函数表达式的步骤基本上是"create a new function object with that function body using the current execution context's lexical environment for the Scope",而命名函数表达式则更复杂:
- 设
funcEnv
为调用NewDeclarativeEnvironment
passing the running execution context’s Lexical Environment作为参数的结果 - 设
envRec
为funcEnv
的环境记录。 - 调用
envRec
的CreateImmutableBinding(N)
具体方法,传递函数的Identifier
作为参数。 - 让
closure
成为创建新 Function 对象 […] 的结果。 将funcEnv
作为 Scope 传入。 - 调用
envRec
的InitializeImmutableBinding(N,V)
具体方法,传递函数的Identifier
和closure
作为参数。 - Return
closure
.
它确实为函数表达式构造了一个额外的包装器环境。在具有块作用域的 ES6 代码中:
var x = function foo(){};
// is equivalent to
var x;
{
const foo = function() {};
x = foo;
}
// foo is not in scope here
What's behind the phenomenon that function name
foo
is accessible inside but invisible outside?
foo
不可变绑定不是在当前执行上下文的词法环境中创建的,而是在仅用于函数表达式周围的闭包的包装器环境中创建的。