Why is a function declaration within a condition block hoisted to function scope in Chrome but not Firefox?
为什么下面的代码在Chrome和Firefox之间输出不同的结果?
1 2 3 4 5 6 7 8 9 | f = function() {return true;}; g = function() {return false;}; (function() { if (g() && [] == ![]) { f = function f() {return false;}; function g() {return true;} } })(); console.log(f()); |
在chrome中:结果是
以上代码的关键行是4行,根据我对函数名提升的了解,函数
我说得对吗?如果是这样,为什么火狐会输出不同的结果?
ECMAScript 5是当前JavaScript语言的官方规范,它没有定义块内函数声明的行为。
引用Kangax:
FunctionDeclarations are only allowed to appear in Program or FunctionBody. Syntactically, they can not appear in Block (
{ ... } ) — such as that ofif ,while orfor statements. This is because Blocks can only contain Statements, not SourceElements, which FunctionDeclaration is. If we look at production rules carefully, we can see that the only way Expression is allowed directly within Block is when it is part of ExpressionStatement. However, ExpressionStatement is explicitly defined to not begin with"function" keyword, and this is exactly why FunctionDeclaration cannot appear directly within a Statement or Block (note that Block is merely a list of Statements).Because of these restrictions, whenever function appears directly in a block (such as in the previous example) it should actually be considered a syntax error, not function declaration or expression. The problem is that almost none of the implementations I've seen parse these functions strictly per rules (exceptions are BESEN and DMDScript). They interpret them in proprietary ways instead.
同样值得引用的是ECMAScript 6草案-B.3.3块级函数声明Web遗留兼容性语义:
Prior to the Sixth Edition, the ECMAScript specification did not define the occurrence of a FunctionDeclaration as an element of a Block statement’s StatementList. However, support for that form of FunctionDeclaration was an allowable extension and most browser-hosted ECMAScript implementations permitted them. Unfortunately, the semantics of such declarations differ among those implementations. [...]
由于ES5不定义块内函数声明的行为,同时允许专有扩展,因此技术上没有"权利"或"错误"。将它们视为"未指定的行为",这在不同的ES5兼容环境中是不可移植的。
不管怎样,这些都很容易重写为可移植代码:
- 是否应将函数声明提升到当前函数/全局范围的顶部?确保函数声明不直接在块内。
- 应该只在块执行时声明函数吗?将函数表达式赋给变量(
var f = function() {}; )。注意,没有提升,并且变量仍然可以在块外部访问(var 声明是函数级范围的)。
根据EcmaScript 6,函数声明是块范围的,因此Firefox实现了正确的ES6-wise行为。