为什么条件块中的函数声明被提升到 Chrome 中的函数范围而不是 Firefox?

IT技术 javascript google-chrome firefox
2021-02-25 20:36:53

为什么以下代码在 Chrome 和 Firefox 之间输出不同的结果?

f = function() {return true;}; 
g = function() {return false;}; 
(function() { 
   if (g() && [] == ![]) { 
      f = function f() {return false;}; 
      function g() {return true;} 
   } 
})(); 
console.log(f());

在 Chrome 中:结果是false. 但是,在 Firefox 中,它是true.

上面代码的关键行是第4行,根据我对函数名提升的了解,该函数g应该在第6行,即第2行被第6行覆盖。IMO,Chrome的行为是正确的。

我是对的吗?如果是这样,为什么 Firefox 会输出不同的结果?

1个回答

ECMAScript 5,JavaScript 语言的当前官方规范,没有定义块内函数声明的行为。

引用Kangax :

FunctionDeclarations只允许出现在ProgramFunctionBody 中在语法上,它们不能出现在Block ( { ... }) 中——例如 of if, whileorfor语句。这是因为只能包含声明,不是SourceElements,这FunctionDeclaration是。如果我们仔细查看产生式规则,我们可以看到Block 中直接允许Expression的唯一方式是当它是ExpressionStatement 的一部分时但是,ExpressionStatement是明确定义的不要以“function”关键字开头,这就是为什么FunctionDeclaration不能直接出现在StatementBlock 中的原因(注意Block只是Statements的列表)。

由于这些限制,无论何时函数直接出现在块中(例如在前面的示例中),它实际上都应该被视为语法错误,而不是函数声明或表达式。问题是我见过的几乎没有一个实现严格按照规则解析这些函数(BESEN 和 DMDScript 除外)。相反,他们以专有的方式解释它们。

还值得引用ECMAScript 6 草案 - B.3.3 块级函数声明 Web Legacy Compatibility Semantics

在第六版之前,ECMAScript 规范没有将FunctionDeclaration的出现定义为 Block 语句的StatementList的元素但是,对这种形式的FunctionDeclaration 的支持是一种允许的扩展,并且大多数浏览器托管的 ECMAScript 实现都允许它们。不幸的是,这些声明的语义在这些实现中有所不同。[...]


由于 ES5 没有定义块内函数声明的行为,同时允许专有扩展,因此在技术上没有“权利”或“错误”。将它们视为“未指定行为”,无法在不同的 ES5 兼容环境中移植。

无论如何,这些很容易重写为可移植代码:

  • 函数声明是否应该被提升到当前函数/全局作用域的顶部?确保函数声明不是直接在块内。
  • 是否应该仅在块执行时声明函数?将函数表达式分配给变量 ( var f = function() {};)。请注意,没有提升并且该变量仍然可以在块外部访问(var声明是函数级范围的)。

根据 ECMAScript 6,函数声明是块范围的,因此 Firefox 实现了正确的 ES6 行为。

在你回答的最后一句话中,你说函数声明是块范围的,你的意思是它们被提升到了它们的块中?因为 MDN 说的正好相反:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
2021-05-01 20:36:53
@pooria 即使 MDN 是这样说的,如果函数在块内,它们也不会被提升到程序的顶部。
2021-05-02 20:36:53
@pooria 还有 MDN 在这里developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... 在这个例子中,如果块在全局范围内不可用,则函数。
2021-05-03 20:36:53