Why the following codes output different results between Chrome and Firefox?
f = function() {return true;};
g = function() {return false;};
(function() {
if (g() && [] == ![]) {
f = function f() {return false;};
function g() {return true;}
}
})();
console.log(f());
In Chrome: the result is false
. However, in Firefox, it is true
.
The key line of the above codes is line 4, and base on my knowledge of function name hoisting, the function g
should be in line 6, namely the line 2 is overridden by line 6. IMO, the behavior of Chrome is correct.
Am I right on this? if so, why Firefox outputs different results?
Why the following codes output different results between Chrome and Firefox?
f = function() {return true;};
g = function() {return false;};
(function() {
if (g() && [] == ![]) {
f = function f() {return false;};
function g() {return true;}
}
})();
console.log(f());
In Chrome: the result is false
. However, in Firefox, it is true
.
The key line of the above codes is line 4, and base on my knowledge of function name hoisting, the function g
should be in line 6, namely the line 2 is overridden by line 6. IMO, the behavior of Chrome is correct.
Am I right on this? if so, why Firefox outputs different results?
Share Improve this question edited Aug 4, 2014 at 4:08 Shog9 160k36 gold badges235 silver badges240 bronze badges asked Aug 4, 2014 at 2:37 zangwzangw 48.5k23 gold badges206 silver badges241 bronze badges 4-
Because Chrome hoists
g
and Firefox doesn't. Don't use function declarations inside blocks. Firefox is actually more correct in this case, but there is still some implementation dependent magic going on. See developer.mozilla/en-US/docs/Web/JavaScript/Reference/… – Felix Kling Commented Aug 4, 2014 at 2:47 - This should be an answer, rather than a ment. (Although I would argue that neither is more or less correct, as the result isn't defined by the spec.) – Ian Clelland Commented Aug 4, 2014 at 2:53
- @IanClelland: I'm pretty sure it's a duplicate, but I'm not feeling searching for it on a phone. – Felix Kling Commented Aug 4, 2014 at 2:55
-
if (g() && [] == ![]) {
could be changed to justif (g()) {
and the example would still work, and be a bit clearer IMHO – chiliNUT Commented Aug 4, 2014 at 3:05
1 Answer
Reset to default 20ECMAScript 5, the current official specification of the JavaScript language, does not define the behavior for function declarations inside blocks.
Quoting 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.
Also worth quoting the 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. [...]
As ES5 does not define the behavior for function declarations inside blocks while allowing proprietary extensions, there are technically no "rights" or "wrongs". Consider them "unspecified behavior" which is not portable across different ES5-pliant environments.
Those are easy to rewrite into portable code anyway:
- Should the function declaration be hoisted to the top of the current function/global scope? Make sure the function declaration is not directly inside of a block.
- Should the function be declared only when the block executes? Assign a function expression to a variable (
var f = function() {};
). Note that there is no hoisting and the variable is still accessible outside of the block (var
declarations are function-level scoped).
As per ECMAScript 6, function declarations are block-scoped, so Firefox implements the correct behavior ES6-wise.