Javascript Hoisting in Chrome And Firefox - javascript

Running this in Chrome and Firefox gives different answers:
(function() {
if(true) {
function f() { alert("yes"); };
} else {
function f() { alert("no"); };
}
f();
})();
In Chrome the result is 'no'
In Firefox the result is 'yes'
Why the difference?

Declaring functions inside conditional statements is non-standard, so do not do that. That's a known issue. You may use function expressions instead of the declarations:
var f;
if(true) {
f = function() { alert("yes"); };
} else {
f = function() { alert("no"); };
}
f();
The famous Kangax article on function expressions gives some additional details:
FunctionDeclarations are only allowed to appear in Program or FunctionBody. Syntactically, they can not appear in Block ({ ... }) — such as that of if, while or for statements. This is because Blocks can only contain Statements, not SourceElements, which FunctionDeclaration is.
The same article also says:
It's worth mentioning that as per specification, implementations are allowed to introduce syntax extensions (see section 16), yet still be fully conforming. This is exactly what happens in so many clients these days. Some of them interpret function declarations in blocks as any other function declarations — simply hoisting them to the top of the enclosing scope; Others — introduce different semantics and follow slightly more complex rules.

From V8 (Chrome JavaScript engine) bug tracker:
Not a bug. Firefox is the only browser that does what you're expecting.
The behavior of Safari and IE on this is the same as Chrome's/V8's.

This happens due to Firefox lack of function hoisting, as conceived in ECMAScript 5.
Chrome correctly assigns a value to f() before running the body of the function,
so the first version of f() is overwritten by the second one.
SpiderMonkey (Firefox’s JavaScript engine) runs the code without pre-assignin a value to f(),
so it uses the only value that encounters on its way: function f() { alert("yes"); };
what's function hoisting?
JavaScript’s function scope means that all variables declared within a function are visible
throughout the body of the function. Curiously, this means that variables are even
visible before they are declared. This feature of JavaScript is informally known as hoisting:
JavaScript code behaves as if all variable declarations in a function (but not any
associated assignments) are “hoisted” to the top of the function.
sources:
http://statichtml.com/2011/spidermonkey-function-hoisting.html
2011 - o'reilly - javascript - the definitive guide 6th edition

Related

What is the definition of "scope" when we are talking about function hoisting in JavaScript [duplicate]

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?
ECMAScript 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 of if, while or for 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-compliant 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.

Scoping of function declarations within `with` statements

Preface: Use of with is discouraged in JavaScript for good reason. It can lead to confusing code, and forward compatibility problems (when new properties are added to built-in objects, for example). This question isn't about whether or not with should be used - it's about the spec-defined behavior of with.
Should the following code work?
let foo = {};
with(foo) {
function bar() {
console.log("hello");
}
}
bar();
It works in Chrome 80, but not in Firefox 72: TypeError: bar is not a function.
Edit: Turns out this error only occurs when pasting into the Firefox console (https://i.imgur.com/WTG3iiX.png), not when running the code within a HTML document.
But notice it's a TypeError, and not a ReferenceError (i.e. bar is not defined). To confirm this we can add console.log("bar" in window) before bar();, and notice that outputs true in Firefox, whereas if you write that before the code it outputs false. So in Firefox the above code has the effect of setting window.bar to undefined.
This works fine in both Firefox and Chrome:
if(true) {
function bar() {
console.log("hello");
}
}
bar();
Just as I'd have expected, since a function foo() {...} declaration is function scoped. So unless there's something weird about with block scopes, it seems like this is a Firefox bug?
I haven't been able to reproduce the problem with Firefox 73, so Firefox's behaviour may have changed.
That said, see MDN on the subject of blocks:
In strict mode, starting with ES2015, functions inside blocks are scoped to that block. Prior to ES2015, block-level functions were forbidden in strict mode.
IIRC, the interaction between hoisting rules and blocks was undefined which resulted in different behaviour in different JS engines. This isn't so much a Firefox bug as a bug in the definition of the language itself.
Avoid function declarations in blocks.

JavaScript function declaration hoisting from within loops [duplicate]

W.r.t Hoisting of fxn definitions.
if (true) {
function foo() {
alert(1)
}
} else {
function foo() {
alert(2)
}
}
foo()
Chrome, some 2-3 months ago - would print 2. Now, it's printing 1. Did I miss something or, did console stop hoisting on fxn's!
DEMO -- prints 1. I'm not sure where to find demo of the older browser version. Probably older v8 engine's node installation?.
Current chrome version - 49
The code you have is invalid in strict mode. Functions don't get hoisted out of blocks (or at least they shouldn't), function declarations inside blocks were completely illegal until ES6. You should write
"use strict";
var foo;
if (true) {
foo = function() {
alert(1)
};
} else {
foo = function() {
alert(2)
};
}
foo()
to get the desired behaviour with reproducible and expected results.
Did I miss something or, did console stop hoisting on fxn's!
Looks like V8 was updated to align with the ES6 spec. It does "hoist" them to the function/top scope, but only when the declaration is actually encountered (in your case, conditionally).
You should avoid using conditionally created functions.
For example, assume the following code:
if (false){
function foo(){
console.log(1)
}
}
foo()
Firefox will not hoist the function and this will result in ReferenceError: foo is not defined. Chrome, however, hoists the function nonetheless and prints 1. So obviously you have deal with different browser behaviour. Therefore, do not do things like that at all (or use function expressions if you really want to).
Also see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function
Functions can be conditionally declared, that is, a function statement can be nested within an if statement. Most browsers other than Mozilla will treat such conditional declarations as an unconditional declaration and create the function whether the condition is true or not, see this article for an overview. Therefore they should not be used, for conditional creation use function expressions.
Especially look at the linked article which somewhat explains the issue you are seeing. So Chrome seems to have changed something in that regard. But again, do not use conditionally created functions.
And note that, as FREEZE commented, you should use 'use strict'; which would not allow such code but throws an exception instead.

Why is a function declaration within a condition block hoisted to function scope in Chrome but not Firefox?

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?
ECMAScript 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 of if, while or for 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-compliant 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.

Why are function declarations handled differently in different browsers?

Although I couldn't find a reference to this easily in google, I'm familiar with the fact that, in javascript, global function declarations get interpreted before any code is executed. In other words, this works fine:
f();
function f() {}
However, I've noticed that chrome and firefox have different interpretations of what a global function declaration is. In particular, chrome is happy reading a function declaration that is inside an if block in the first pass, but firefox is not.
try {document.write(f);} // works in chrome
catch(e) {document.write(e.message);} // throws an error in firefox
try {document.write(g);} // works in chrome and firefox
catch(e) {document.write(e.message);}
if(true) function f() {}
function g() {}
You can try this example yourself with this fiddle. I'm using Chrome 16.0.912.75 and Firefox 9.0.1.
What is the ECMA standard for this behavior? Is there a term for this process of "lifting" function declarations above other code? Is what code gets "lifted" open to interpretation (are both browsers right)? Or is it a bug in one of them?
This answer is outdated since the release of ES6 in 2015. See What are the precise semantics of block-level functions in ES6? for how it works since then.
Function declarations are not valid in blocks. You have undefined behaviour which is undefined.
Function declarations at a top level (either global or top level within a function) are hoisted.
Function declarations inside blocks are a syntax error in strict mode
(function () {
"use strict";
if (true) {
function g() { }
}
})();
SyntaxError: In strict mode code, functions can only be declared at top level or immediately within another function.
The ECMA standard for this behavior is to throw a SyntaxError when parsing the script. Unfortunately doing that is not compatible with the web as Raynos says.
See Which JS function-declaration syntax is correct according to the standard? for some extended discussion on the issue.

Categories

Resources