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

Posted

技术标签:

【中文标题】为啥条件块中的函数声明在 Chrome 而不是 Firefox 中提升到函数范围?【英文标题】:Why is a function declaration within a condition block hoisted to function scope in Chrome but not Firefox?为什么条件块中的函数声明在 Chrome 而不是 Firefox 中提升到函数范围? 【发布时间】:2014-09-26 11:29:12 【问题描述】:

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

【问题讨论】:

因为 Chrome 会提升 g 而 Firefox 不会。不要在块内使用函数声明。在这种情况下,Firefox 实际上更正确,但仍然存在一些依赖于实现的魔法。见developer.mozilla.org/en-US/docs/Web/javascript/Reference/… 这应该是一个答案,而不是评论。 (尽管我认为这两者都或多或少都不正确,因为规范没有定义结果。) @IanClelland:我很确定它是重复的,但我不想在手机上搜索它。 if (g() && [] == ![]) 可以更改为 if (g()) 并且该示例仍然有效,并且更清晰一些恕我直言 【参考方案1】:

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

引用Kangax:

FunctionDeclarations 只允许出现在 ProgramFunctionBody 中。从语法上讲,它们不能出现在 Block ( ... ) 中——例如 ifwhilefor 语句中的。这是因为Blocks 只能包含Statements,而不能包含FunctionDeclarationSourceElements。如果我们仔细查看生产规则,我们会发现 Expression 直接在 Block 中被允许的唯一方式是它是 ExpressionStatement 的一部分。但是,ExpressionStatement 被明确定义为不以“function”关键字开头,这正是 FunctionDeclaration 不能直接出现在 中的原因StatementBlock(请注意,Block 只是 Statements 的列表)。

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

也值得引用ECMAScript 6 draft - B.3.3 Block-Level Function Declarations 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/… @pooria 即使 MDN 这么说,如果函数在块内,它们也不会被提升到程序的顶部。 @pooria 也是 MDN 这里的 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 在这个例子中,如果块在全局范围内不可用,则函数。

以上是关于为啥条件块中的函数声明在 Chrome 而不是 Firefox 中提升到函数范围?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在一个函数中声明的联合类型在另一个函数中使用无效?

为啥这是 C++ 中的有效函数声明? [复制]

为啥这个 LSEP 符号出现在 Chrome 而不是 Firefox 或 Edge 上?

为啥这个 LSEP 符号出现在 Chrome 而不是 Firefox 或 Edge 上?

为啥我的代码可以在 chrome 上运行,而不是一次使用 cordova 编译

为啥 Chrome 对某些资源使用 Http/1.1 而不是 H2