f = function() {return true;}; g = function() {return false;}; (function() { if (g() && [] == ![]) { f = function f() {return false;}; function g() {return true;} } })(); console.log(f());
在Chrome中:结果是假的.但是,在Firefox中,这是真的.
上述代码的关键是第4行,根据我对功能名称提升的知识,函数g应该在第6行,即第2行被第6行覆盖.IMO,Chrome的行为是正确的.
我是对的吗如果是这样,为什么Firefox输出不同的结果?
解决方法
报价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 draft – B.3.3 Block-Level Function Declarations Web Legacy Compatibility Semantics:
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的正确行为.