最近我遇到了一些关于命名函数表达式(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 的精彩帖子并在那里提到了这种现象。最让我吃惊的是,一个 NFE 的函数名不能与函数体中的其他值重新关联。例如,
(function foo() {
foo = 5;
alert(foo);
})(); // will alert function code instead of 5
在上面的例子中,我们尝试foo
用另一个值重新绑定标识符5
。但这失败了!而我翻到 ES5 Spec 发现在创建 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 规范对此有所了解吗?我在网上找不到太多讨论。