问题 为什么条件块中的函数声明在Chrome中升级到功能范围而不是Firefox?


为什么以下代码在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输出不同的结果?


6197
2017-08-04 02:37


起源

因为Chrome升降机 g 和Firefox没有。不要在块内使用函数声明。在这种情况下,Firefox实际上更正确,但仍然存在一些依赖于实现的魔法。看到 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... - Felix Kling
这应该是答案,而不是评论。 (虽然我认为两者都没有或多或少是正确的,因为结果没有被规范定义。) - Ian Clelland
@IanClelland:我很确定这是重复的,但我不觉得在手机上搜索它。 - Felix Kling
if (g() && [] == ![]) {  可以改成只是 if (g()) {  并且这个例子仍然有效,并且恕我直言 - chiliNUT


答案:


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

引用 Kangax

FunctionDeclarations 只允许出现在 程序 要么 函数体。从语法上讲,他们 不能出现  ({ ... }) - 比如 ifwhile 要么 for 声明。这是因为  只能包含 声明不是 SourceElements, 哪一个 FunctionDeclaration 是。如果我们仔细研究生产规则,我们就能看到唯一的方法 表达 允许直接进入  是什么时候它的一部分 ExpressionStatement。然而, ExpressionStatement 是明确定义的 不以“function”关键字开头,这正是原因 FunctionDeclaration 不能直接出现在 声明 要么  (注意  只是一个清单 声明)。

由于这些限制,只要函数直接出现在一个块中(例如在前面的例子中),它实际应该是 考虑语法错误,而不是函数声明或表达。问题是我见过的几乎没有一个实现严格按照规则解析这些函数(例外是BESEN和DMDScript)。他们用专有的方式解释它们。

也值得引用 ECMAScript 6草案 - B.3.3块级函数声明Web遗留兼容性语义

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


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

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

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

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


15
2017-08-04 02:59