我们有这样的东西
if(true) {
const a = 1;
function myFunc() {
alert(a);
}
myFunc();
}
在 Safari 11 中,这会导致“ReferenceError:找不到变量:a”
。
相同的代码在 Chrome 和 Firefox 中运行不会出现错误。
在 Safari 中使用“严格模式”
解决该问题。
我认为主要问题是 const a
和 function myFunc
的范围不同。事实上,最后一个函数是一个全局函数,因为条件语句不会像为 let 和 const 那样为其中的函数创建 block 作用域(我想是出于遗留原因)。
我想知道 Safari 在这种情况下是否正确,因为我们混合了不同范围的内容。
是否有一些官方资源可以解释此案例?我在 caniuse 和 mdn 网站上都没有发现任何提及此行为的信息
最佳答案
多年来,规范中都没有定义 block 内的函数声明,但不同的 JavaScript 引擎允许它们。
由于该语法未在规范中定义并且被 javascript 引擎所允许,因此不同的引擎会执行不同的操作。有些将其视为语法错误,另一些则将 block 作用域中的函数声明视为函数表达式。某些引擎将 block 作用域中的函数声明视为同一作用域中的多个提升声明。
从 ES2015 开始,函数声明是规范的一部分,有两种处理方式:
- 标准网络语义
- 旧版网络语义
标准语义
使用标准语义,函数声明将转换为函数表达式,使用 let
关键字进行声明,并提升到 block 的顶部。标准语义在严格模式下生效。
因此,在严格模式下,JavaScript 引擎将处理您的代码,就好像它是这样编写的:
if(true) {
let myFunc = function() {
alert(a);
}
const a = 1;
myFunc();
}
旧版 Web 语义
在浏览器的非严格模式下,旧版 Web 语义适用。当 block 作用域中的函数声明不被视为语法错误时,有three scenarios所有主要 javascript 引擎都以相同的方式处理它们。这三种情况是:
- 函数在单个 block 内声明和引用
- 函数在单个 block 内声明并可能使用,但也由不包含在同一 block 内的内部函数定义引用。
- 函数在单个 block 中声明并可能使用,但也在其中引用 后续区 block 。
除了 block 作用域中定义的函数的 let
变量之外,在包含函数作用域或全局作用域中还有一个使用 var
定义的变量。此 var
赋值不会提升到 block 的顶部,而是在代码中到达函数声明时完成。
JavaScript 引擎将非严格模式下的代码视为:
var varMyFunc;
if(true) {
let myFunc = function() {
alert(a);
}
const a = 1;
varMyFunc = myFunc; // at the place of function declaration
myFunc();
}
您不应编写依赖于旧版 Web 语义的代码。相反,请使用严格模式来确保您的代码依赖于处理 block 作用域中的函数声明的标准规则。话虽如此,如果您有依赖于旧版 Web 语义的非严格模式下的遗留代码,您可以期望它能够跨浏览器工作。
关于javascript - 在条件语句中混合函数和 const/let 会导致 Safari 中出现 ReferenceError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63810931/