ES6中块级函数的精确语义是什么?

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

本文关键字:语义 是什么 函数 ES6      更新时间:2023-09-26

我试图通过阅读原始规范来理解ES6中新的标准化块级函数,我的肤浅理解是:

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

然而,这是进一步复杂的事实,这些语义的一部分被指定为"可选的",只有强制性的web浏览器(附件B)。所以我想有以下表格填写:

<>之前| block外可见?|升起?到哪一点?|"TDZ"?|------------------------------------------------------------------------------------------------------------------------|非严格模式,无"web扩展"| | | ||严格模式,无"web扩展"| | | ||非严格模式,带有"web扩展| | | |"|严格模式,带有"web扩展"| | | |之前

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

1. If strict is false, then
...

但是在我看来,strict是指函数对象的[[Strict]]内部槽位。这是否意味着:

// Non-strict surrounding code
{
    function foo() {"use strict";}
}
在上表中

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

请记住,我最感兴趣的是ES6规范本身,而不是实际实现的不一致性。

我看到的strict是指函数对象的[[Strict]]内部槽位。

。是的。它指的是函数(或脚本)的严格性,其中包含函数声明的块出现。不符合要声明(或不声明)的函数的严格性。

"web扩展"只适用于草率的(非严格的)代码,并且只适用于函数语句的外观是"合理的"——也就是说,例如,如果它的名称不与形式参数或词法声明的变量冲突。

请注意,没有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
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表示)具有相同的名称。所以现在我可以简洁地回答你的问题了:

块外可见?

是的,像var。然而,有第二个绑定只在块内可见。

升起?

是-两次。

到哪一点?

函数(用undefined初始化)和块(用函数对象初始化)。

"TDZ"?

不是词法声明的变量(let/const/class)在引用时抛出的时间死区,不是。但是在执行函数体时遇到函数声明之前,函数作用域的变量是undefined(特别是在块之前),如果您尝试调用它,也会得到异常。


仅供参考:在ES6中,上述行为仅为函数作用域中的块指定。自ES7以来,同样适用于eval和全局作用域中的块。

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