在条件语句中混合函数和 const/let 导致 Safari 中的 ReferenceError

Mixing functions and const/let inside conditional statements cause ReferenceError in Safari

我们有这样的东西

if(true) {
    const a = 1;
    function myFunc() {
        alert(a);
    }

    myFunc();
}

在 Safari 11 中,这会导致 "ReferenceError: Can't find variable: a"

相同的代码在 Chrome 和 Firefox 中运行没有错误。

在 Safari 中使用 "strict mode" 解决问题。

我认为主要问题是const a 和function myFunc 的作用域不同。最后一个实际上是一个全局函数,因为条件语句不会像 let 和 const 那样为其内部函数创建块作用域(我想是出于遗留原因)。

我想知道 Safari 在这种情况下是否正确,因为我们正在混合不同范围的东西。

是否有一些官方资源可以解释这种情况?我在 caniuse 和 mdn 网站上都没有发现任何关于此行为的提及

块内的函数声明多年来未在规范中定义,但不同的 javascript 引擎允许它们。

由于此语法未在规范中定义并且被 javascript 引擎允许,因此不同的引擎会执行不同的操作。有些人将其视为语法错误,其他人将块作用域中的函数声明视为函数表达式。一些引擎将块作用域中的函数声明视为同一作用域中的多个提升声明。

从 ES2015 开始,函数声明是规范的一部分,它们有两种处理方式:

  • 标准网络语义
  • 旧版网络语义

标准语义

使用标准语义,函数声明被转换为函数表达式,使用 let 关键字声明并提升到块的顶部。标准语义在严格模式下有效。

因此在严格模式下,您的代码将被 javascript 引擎处理,就好像它是这样写的:

if(true) {
    let myFunc = function() {
       alert(a);
    }

    const a = 1;
    myFunc();
}

遗留网络语义

在浏览器的 non-strict 模式下,旧版网络语义适用。当块范围内的函数声明不被视为语法错误时,所有主要 javascript 引擎都以相同的方式处理 three scenarios。这三种情况是:

  1. 函数在单个块中声明和引用
  2. 函数在单个块中声明并可能使用,但也被不包含在同一块中的内部函数定义引用。
  3. 一个函数被声明并可能在单个块中使用,但也在其中被引用 后续区块。

除了在块作用域中定义的函数的 let 变量外,在包含函数作用域或全局作用域中还有一个用 var 定义的变量。此 var 赋值不会提升到块的顶部,而是在代码中到达函数声明时完成。

您在 non-strict 模式下的代码被 javascript 引擎视为:

var varMyFunc;

if(true) {
    let myFunc = function() {
       alert(a);
    }

    const a = 1;

    varMyFunc = myFunc;    // at the place of function declaration
   
    myFunc();
}

您不应编写依赖于遗留网络语义的代码。相反,使用严格模式来确保您的代码依赖于标准规则来处理块作用域中的函数声明。话虽如此,如果您在 non-strict 模式下有依赖于遗留 Web 语义的遗留代码,您可以期望它能够工作 cross-browser.