一尘不染

JavaScript ES6中块级功能的确切语义是什么?

javascript

我正在尝试通过阅读原始规范来围绕ES6中新的标准化块级功能进行研究。我的肤浅理解是:

  • ES6中允许块级函数声明。
  • 他们吊在街区的顶部。
  • 在严格模式下,它们在包含块之外不可见。

但是,由于这些语义的一部分被指定为“可选”且仅对于Web浏览器是强制性的(附件B),这使情况进一步复杂化。所以我想填写下表:

|  Visible outside of block?  |  Hoisted? Up to which point?  |   "TDZ"? |
------------------------------------------------------------------------------------------------------------------------
|   Non-strict mode,   no "web extensions"   |                             |                               |          |
|   Strict mode,       no "web extensions"   |                             |                               |          |
|   Non strict mode,   with "web extensions  |                             |                               |          |
|   Strict mode,       with "web extensions" |                             |                               |          |

我也不清楚在这种情况下“严格模式”是什么意思。这种区别似乎在附件B3.3中引入,作为在运行时执行函数声明的一些附加步骤的一部分:

1. If strict is false, then
...

但是,据我所知,strict是指[[Strict]]功能对象的内部插槽。这是否意味着:

// Non-strict surrounding code

{
    function foo() {"use strict";}
}

在上表中应被视为“严格模式”?但是,这与我最初的直觉相矛盾。

请记住,无论实际的实现不一致如何,我都对ES6规范本身最感兴趣。


阅读 411

收藏
2020-04-25

共1个答案

一尘不染

据我所知,strict是指[[Strict]]功能对象的内部插槽。

不,是的。它确实是指函数或脚本的严格性,其中包含函数声明的块出现在其中。不严格于要(或不要)声明的功能。

“ Web扩展”仅适用于草率的(非严格)代码,并且仅在函数语句的外观为“合理”的情况下(例如,其名称与形式参数不冲突或在词法上不匹配)声明的变量。

注意,没有网络兼容性语义的严格代码和草率代码之间没有区别。在纯ES6中,块中的函数声明只有一种行为。

所以我们基本上有

                 |      web-compat               pure
-----------------+---------------------------------------------
strict mode ES6  |  block hoisting            block hoisting
sloppy mode ES6  |  it's complicated ¹        block hoisting
strict mode ES5  |  undefined behavior ²      SyntaxError
sloppy mode ES5  |  undefined behavior ³      SyntaxError

1:请参见下文。要求警告。
2:通常SyntaxError抛出a。3:ES5.1§12中的注释谈到“实现之间的重大和不可调和的变化”(例如这些)。建议警告。

那么,具有Web兼容性的ES6实现如何在具有传统语义的草率模式函数中的块中的函数声明中起作用?
首先, 纯语义仍然适用 。即,函数声明被提升到词法块的顶部。但是,还有一个var声明悬挂在封闭函数的顶部。
当评估函数声明时(在块中,就像是一条语句一样被满足),函数对象将 分配 给该函数作用域变量。

这可以通过代码更好地解释:

function enclosing(…) {
    …
    {
         …
         function compat(…) { … }
         …
    }
    …
}

与…相同

function enclosing(…) {
    var compat₀ = undefined; // function-scoped
    …
    {
         let compat₁ = function compat(…) { … }; // block-scoped
         …
         compat₀ = compat₁;
         …
    }
    …
}

是的,这有点令人困惑,因为它们具有两个具有相同名称的不同绑定(用下标0和1表示)。现在,我可以简洁地回答您的问题:

Visible outside of block?

是的,就像var。但是,还有第二个绑定仅在块内部可见。

Hoisted?

是的-两次。

Up to which point?

函数(无论如何都使用初始化undefined)和块(都通过函数对象初始化)。

“ TDZ”?

不是在引用上抛出的按词法声明的变量(let/const/class)的时间盲区上,不是。但是,在执行主体时遇到函数声明之前,函数作用域变量是undefined(尤其是在块之前),并且如果尝试调用它,也会获得异常。


仅供参考:在ES6中,仅针对函数作用域中的块指定了上述行为。从ES7开始,这同样适用于eval全局范围内的块。

2020-04-25