关于javascript:ES6中块级函数的精确语义是什么?

What are the precise semantics of block-level functions in ES6?


我试图通过阅读原始规范来围绕ES6中新的标准化块级功能。我的肤浅理解是:

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


然而,由于这些语义的一部分被指定为"可选的"并且仅对于Web浏览器是必需的(附件B),因此这进一步复杂化。所以我想填写下表:

1
2
3
4
5
6
                                             |  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
2
1. If strict is false, then
...


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

1
2
3
4
5
// Non-strict surrounding code

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


应该被认为是上表中的"严格模式"?然而,这与我最初的直觉相矛盾。


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


As far as I can see, strict refers to the [[Strict]] internal slot of the function object.


没有。是的。它确实指的是包含函数声明的块发生的函数(或脚本)的严格性。不是要声明(或不是)声明的函数的严格性。


"web扩展"仅适用于草率(非严格)代码,并且仅当函数语句的外观是"理智"时 - 例如,如果其名称不与形式参数或词法冲突声明变量。


请注意,没有Web兼容性语义,strict和sloppy代码之间没有区别。在纯ES6中,块中的函数声明只有一种行为。


所以我们基本上有

1
2
3
4
5
6
                 |      web-compat               pure
-----------------+---------------------------------------------
strict mode ES6  |  block hoisting            block hoisting
sloppy mode ES6  |  it's complicated 1        block hoisting
strict mode ES5  |  undefined behavior 2      SyntaxError
sloppy mode ES5  |  undefined behavior 3      SyntaxError


1:见下文。要求警告。
2:通常,抛出SyntaxError
3:ES5.1§12中的注释说明了"实现之间的重大且不可调和的变化"(例如这些)。建议使用警告。


那么现在具有Web兼容性的ES6实现如何在具有遗留语义的草率模式函数中的块中进行函数声明?
首先,纯语义仍然适用。也就是说,函数声明被提升到词块的顶部。
但是,还有一个var声明被提升到封闭函数的顶部。
并且当评估函数声明时(在块中,就好像它像声明一样),将函数对象分配给该函数范围的变量。


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

1
2
3
4
5
6
7
8
9
function enclosing() {
    …
    {
         …
         function compat() {}
         …
    }
    …
}


与...一样的工作

1
2
3
4
5
6
7
8
9
10
11
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)的临时死区的意义上,没有。但是在执行body之前遇到函数声明之前,函数范围的变量是undefined(特别是在块之前),如果你试图调用它,你也会得到一个异常。


仅供参考:在ES6中,仅针对功能范围中的块指定了上述行为。由于ES7同样适用于eval和全局范围中的块。



我不确定你的困惑来自哪里。根据10.2.1,非常清楚"在严格模式下"是什么或不是什么。在您的示例中,foo s [[Strict]]内部插槽确实true并且将处于严格模式,但托管它的块不会。第一句(您引用的那个)与托管块有关,而不是与其中生成的内容有关。片段中的块不是严格模式,因此该部分适用于它。