JavaScript中存储的命名函数表达式中标识符的不可变绑定记录在哪里?

JavaScript中存储的命名函数表达式中标识符的不可变绑定记录在哪里?,javascript,ecmascript-5,ecma262,function-expression,Javascript,Ecmascript 5,Ecma262,Function Expression,最近我遇到了一些关于命名函数表达式(NFE)的有趣事实。据我所知,NFE的函数名可以在函数体中访问,这使得递归更加方便,并为我们节省了参数。被调用方。函数名在函数体之外不可用。比如说, var foo = function bar() { console.log(typeof bar); }; typeof foo; // 'function' typeof bar; // 'undefined', inaccessible outside the NFE foo(); // 'fu

最近我遇到了一些关于命名函数表达式(NFE)的有趣事实。据我所知,NFE的函数名可以在函数体中访问,这使得递归更加方便,并为我们节省了
参数。被调用方
。函数名在函数体之外不可用。比如说,

var foo = function bar() {
    console.log(typeof bar);
}; 

typeof foo; // 'function'
typeof bar; // 'undefined', inaccessible outside the NFE
foo(); // 'function', accessible inside the NFE
(function foo() {
    foo = 5;
    alert(foo);
})(); // will alert function code instead of 5
var foo = 1;
(function foo() {
    alert(foo);
})(); // will alert function code rather than 1
alert(foo); // 1
这是一个有据可查的特性,kangax对NFE有着极好的理解,并在那里提到了这种现象。最让我吃惊的是NFE的函数名不能与函数体中的其他值重新关联。比如说,

var foo = function bar() {
    console.log(typeof bar);
}; 

typeof foo; // 'function'
typeof bar; // 'undefined', inaccessible outside the NFE
foo(); // 'function', accessible inside the NFE
(function foo() {
    foo = 5;
    alert(foo);
})(); // will alert function code instead of 5
var foo = 1;
(function foo() {
    alert(foo);
})(); // will alert function code rather than 1
alert(foo); // 1
在上面的示例中,我们尝试用另一个值
5
重新绑定标识符
foo
。但这失败了!我转向ES5规范,发现创建了一个不可变的绑定记录,并在创建NFE时将其添加到词汇环境的环境记录中

问题是,当NFE在函数体中引用自己的函数名时,该名称被解析为自由变量。在上面的示例中,
foo
在NFE中引用,但它既不是该函数的形式参数,也不是该函数的局部变量。所以它是一个自由变量,它的绑定记录可以通过NFE的[[scope]]属性解析

这样考虑,如果在外部范围内有另一个具有相同名称的标识符,则似乎有一些冲突。比如说,

var foo = function bar() {
    console.log(typeof bar);
}; 

typeof foo; // 'function'
typeof bar; // 'undefined', inaccessible outside the NFE
foo(); // 'function', accessible inside the NFE
(function foo() {
    foo = 5;
    alert(foo);
})(); // will alert function code instead of 5
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规范来解释一下吗?我在网上找不到太多的讨论

    函数名的不可变绑定记录存储在哪里

    在无法看到的额外词法环境记录中:-)

    在NFE内部解析时,函数名
    foo
    为什么会超过
    var foo=1

    事实上,事实并非如此。您可以在函数作用域中声明一个新的本地
    var foo
    ,而不发生任何冲突,但是如果您不这样做,那么free
    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
    }());
    
    它们的绑定记录是否存储在同一词汇环境中

    不是。每个命名函数表达式都包含在一个额外的词法环境中,该环境具有一个单一的、不可变的绑定,用于初始化函数名

    这在规范的章节中进行了描述。虽然函数声明和匿名函数表达式的步骤基本上是“使用当前执行上下文的词法环境为作用域创建具有该函数体的新函数对象”,但命名函数表达式更为复杂:

  • funcev
    作为调用的结果,并将正在运行的执行上下文作为参数传递
  • envRec
    成为
    funcEnv
    的环境记录
  • 调用
    envRec
    的具体方法,将函数的
    标识符作为参数传递
  • closure
    作为创建新函数对象[…]的结果传入
    funcev
    作为作用域。
  • 调用
    envRec
    的具体方法,传递函数的
    Identifier
    closure
    作为参数
  • 返回
    关闭
  • 它确实为函数表达式构造了一个额外的包装器环境。在具有块作用域的ES6代码中:

    var x = function foo(){};
    // is equivalent to
    var x;
    {
        const foo = function() {};
        x = foo;
    }
    // foo is not in scope here
    
    函数名
    foo
    内部可访问,外部不可见的现象背后是什么


    foo
    不可变绑定不是在当前执行上下文的词法环境中创建的,而是在包装器环境中创建的,包装器环境仅用于函数表达式的闭包。

    Wow,很好的答案,@Bergi!谢谢你澄清我的疑问!