javascript - JavaScript 中真的有 block 作用域吗(作为一流的语言概念)?

标签 javascript

正如我们所知,传统上 JS 一直缺乏 block 作用域。正如我们所知,直到最近,JS 才只有函数作用域。

虽然在最近的 JS 版本中,我们可以使用 letconst 变量,它们仅在定义它们的范围内可见。

但是……在内心深处……这究竟是如何完成/实现的?现在语言中是否真的有 JS 中 block 作用域的一流概念,或者 block 作用域只是一些模拟,使某些变量仅在定义它们的 block 中可见?

我的意思是在最近的 JS 版本中 block 作用域是一流的东西,就像函数作用域一样,或者 block 作用域只是某种模拟,而我们实际上仍然只有旧的好函数范围?

最佳答案

But... deep down... how is this done/implemented really? Is there really now in the language a first-class notion of block scope in JS...?

是的,有。一个新 block ¹创建一个新的 lexical environment与创建函数的方式相同(显然,没有创建函数的所有其他方面)。你可以在 Evaluation section 中看到规范中的 block 数。

这是一流的语言结构。


¹ 我最初写“...包含一个letconstclass 声明...” 但是规范实际上并没有做出这种区分,尽管我希望 JavaScript 引擎会这样做(因为如果没有词法声明的绑定(bind)就不需要新的环境)。


在您提出的评论中:

What about hoisting? I read that block scoped-variables are hoisted to the top of the block they are defined in... but then also... You get an error if you try to access a block-scoped variable before the line/statement where it is declared in its block? This sounds contradictory, no? If they are hoisted we would not be getting this error but we would be getting undefined. What is the truth here?

my book我将它们描述为半提升:变量的创建(更一般地说,“绑定(bind)”)被提升到其声明范围的顶部(let x 或其他)出现(在本例中为 block ),但绑定(bind)不会初始化,直到在逐步执行代码。创建和初始化之间的时间称为时间死区。您不能(根本)在 TDZ 内使用绑定(bind)。

这仅适用于 letconstclass 声明。 var 有两种不同的处理方式: 1. 显然,var 被提升到函数(或全局)作用域的顶部,而不仅仅是 block 作用域。 2. 不太明显的是,var 绑定(bind)在进入作用域时同时创建 初始化(值为undefined)。它们已完全吊起。 (它们的声明是;var 语句上的任何初始化器实际上是一个赋值,并在代码的逐步执行中到达该语句时完成.)

这是一个例子:

function foo(n) {
    // `l1` and `l2` don't exist here at all here
    // `v` is created and initialized with `undefined` here, so we can happily
    // do this:
    console.log(`v = ${v}`);
    if (n > 10) {
        // `l1` and `l2` are created here but not initialized; if we tried to
        // use them here, we'd get an error; uncomment this line to see it:
        // console.log(`l1 = ${l1}`);
        console.log("n is > 10");
        var v = "a";  // `v` is assigned the value `"a"` here, replacing the
                      // value `undefined`
        let l1 = "b"; // `l1` is initialized with the value `"b"` here
        console.log(`l1 = ${l1}`);
        let l2;       // `l2` is initialized with the value `undefined `here
        console.log(`l2 = ${l2}`);
        l2 = "c";     // `l2` is assigned the value `"c"` here, replacing the
                      // value `undefined`
        console.log(`l2 = ${l2}`);
    }
}
foo(20);

为了完整起见,function 声明也是完全提升的,但比 var 更甚:函数实际上是在进入作用域(与 var 不同,后者获取值 undefined)。


在您观察到的评论中:

Then... I don't see what's the difference between no hoisting and half-hoisting...

很好,我没有解释。区别与外部作用域中的隐藏 标识符有关。考虑:

function foo() {
    let a = 1;
    if (/*...*/) {
        console.log(`a = ${a}`);
        let a = 2;
        // ...
    }
}

日志应该显示什么?

抱歉,这是个骗人的问题;日志没有显示任何内容,因为您尝试在那里使用 a 时出错,因为内部 a 声明 shadows (隐藏)外部 a 声明,但内部 a 尚未初始化,因此您还不能使用它。它在 TDZ 中。

可以可能使外部a在那里可访问,或者使内部a在那里可访问,值为undefined(例如,像 var 一样完全提升它,但只是在 block 内),但这两个都有 TDZ 帮助解决的问题。 (特别是:使用外部 a 会让程序员感到困惑 [a 在 block 的开头意味着一件事,但在后面又意味着另一件事?!] 并且意味着 JavaScript引擎必须在所有地方创建新的词法环境,基本上每个 letconstclass 都会引入一个新词法环境。并且预初始化undefined 让程序员感到困惑,因为 var 多年来向我们展示了...)

关于javascript - JavaScript 中真的有 block 作用域吗(作为一流的语言概念)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58622919/

相关文章:

javascript - 如何使用JavaScript模板?

javascript - 如何为 Google Charts 中的特定列添加颜色

javascript - 带有变量的 Backbone js动态事件

javascript - 当值是reduce函数中的对象时访问属性(CouchDB)

javascript - 在 ng-repeat、验证中选择第一个单选按钮

javascript - 使用 window.open 在新窗口中设置选项卡

javascript - JQuery 库中的特殊值(value)

javascript - 如何动态创建 JavaScript 数组(JSON 格式)?

javascript - 如何格式化嵌套 json 数组取决于 api 请求 json 的条件

javascript - 尝试在计时器结束后停止计算点击次数