为什么块作用域函数在范围之外起作用?

时间:2018-12-24 13:31:22

标签: javascript ecmascript-6

我正在读Kyle Simpson:ES6 & Beyond,我在second chapter。在标题为“块范围函数”的部分中给出了一个示例:

{
    foo();                  // works!

    function foo() {
        console.log("works!");
    }
}

foo();                      // ReferenceError

(出于测试目的,我添加了console.log。)

我希望该代码在第一次调用时成功运行foo,即使该函数在调用下面定义,并且由于参考错误而在第二次调用时崩溃,正如本书所声称的那样。但是,如果我在Chromium(版本71.0.3578.98(正式版本)快照(64位))中运行此命令,则两个function调用都会成功执行,并且"works!"会两次输出到控制台。如果我在FireFox中运行相同的脚本,则范围之外的第一个函数调用不会记录任何内容,而第二个函数调用将输出"works!"。因此,Chromium和FireFox的行为彼此之间以及从书中得出的期望都不相同。我已经在babeljs.io中转译了此文件,结果是:

"use strict";

{
    // works!

    var _foo = function _foo() {
        console.log("works!");
    };

    _foo();
}

foo(); // ReferenceError

奇怪的是,foo is定义为function范围(使用关键字var代替关键字letconst),因此可以访问从作用域外部进行调用,但作用域外部的调用未更改为_foo,因此会出现错误,但是如果将脚本更改为

{
    foo();                  // works!

    function foo() {
        console.log("works!");
    }
}

window["foo"]();                        // ReferenceError

编译器不会将foo的名称更改为_foo。通过所有这些实验,我得出的结论是,如果我打算拥有一个块作用域函数,则需要明确定义它,例如:

{
    foo();                  // works!

    let foo = function() {
        console.log("works!");
    };
}

foo();                      // ReferenceError

,但是随后由于临时死区问题,甚至对foo的第一次调用也崩溃了。因此,我的问题是:为什么块范围的function在范围之外起作用?

1 个答案:

答案 0 :(得分:1)

正如凯尔(Kyle)的书中所提到的那样,块中函数声明的语义过去在实现之间有所不同。 (这也许可以解释为什么在编码样式注释中通常不建议在块内声明函数。)

浏览器通常希望尽可能多的旧代码工作,因此通过 not 这样相当重要的更改,将块嵌套的function声明提升到包含函数范围的旧代码肯定可以打破。因此,由于缺少更好的选择,"use strict";标志将ES6语义“打开”。旧代码(可能)在任何地方都不会有"use strict";,因此它将继续采用旧方法(当然,这总是很危险的)。