5

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

Vivek Chandra
  • 4,240
  • 9
  • 33
  • 38
  • How does the second `foo()` definition ever get executed? Surely true is always true... – Jazcash Nov 18 '16 at 12:49
  • 2
    @Jazcash That's the beauty of hoisting. Just because the code inside the `else` statement doesn't get executed doesn't mean the function definition does not get processed. – krillgar Nov 18 '16 at 12:51
  • 3
    That code will cause errors in strict mode. Defining functions in conditional clauses like that is bad practice. – Pointy Nov 18 '16 at 12:54
  • I just gave an ex for hoisting. Just an ex – Vivek Chandra Nov 18 '16 at 12:54
  • 1
    Wow, you're right, I just tested in Chrome 37 on Browserstack and it does indeed print 2. I'm perplexed, guess I should do more JS homework! – Jazcash Nov 18 '16 at 12:56
  • @Pointy It doesn't throw exceptiOns here, but I agree that it's bad –  Nov 18 '16 at 12:56
  • @FREEZE Actually it does. I have it in a separate file, and when you add `'use strict';` before `if (true)`, then when it gets to `foo()`, an error is thrown saying `"foo is not defined"`. – krillgar Nov 18 '16 at 12:58
  • @krillgar But I tested it here, it doesn't throw this exception, or my browser is different than ur? –  Nov 18 '16 at 12:59
  • Related doc: http://www.ecma-international.org/ecma-262/5.1/#sec-10.5 – Caramiriel Nov 18 '16 at 13:02
  • @FREEZE I'm on Chrome v54. I tried it within the Fiddle, and it threw a console error as well. – krillgar Nov 18 '16 at 13:03
  • @krillgar I've tried now and it worked, weird, I've now received the same exception –  Nov 18 '16 at 13:04
  • 1
    Why are you writing code like this? –  Nov 18 '16 at 13:26

2 Answers2

7

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.

str
  • 42,689
  • 17
  • 109
  • 127
  • You could also note that in strict mode it'd also result in this ReferenceError. –  Nov 18 '16 at 13:06
  • @FREEZE Good catch, I added a comment. – str Nov 18 '16 at 13:34
  • @str -- thanks a ton. Was looking for that article. " Chrome seems to have changed something in that regard" -- I wanted to know what that change is. I know that we've to use fxn expressions. As Bergi says - looks like hoisting's nature has changed – Vivek Chandra Nov 18 '16 at 13:49
  • @VivekChandra Maybe you will find something in the release notes or rather the change log. – str Nov 18 '16 at 19:52
7

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).

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 1
    So, now -- with ES6 -- this isnt valid? " Note that other clients interpet `foo` as function declaration here, overwriting first `foo` with the second one, and producing "2", not "1" as a result" -- this was given in the link @str has answered. – Vivek Chandra Nov 18 '16 at 13:42
  • Also, in sloppy mode - up until v8 got an ES6 upgrade, fxn's declarations inside blocks was valid. From what u say, hoisting works - but, it has to be encountered. – Vivek Chandra Nov 18 '16 at 13:46
  • @VivekChandra It never was valid in the first place. In ES5 strict mode, it threw a syntax error. [Function statements and similar things](http://kangax.github.io/nfe/#function-statements) were proprietary extensions that worked only in sloppy mode, but differently in every engine. ES6 standardised function declarations that hoist to *block scope*, and also specified a web-compat way for putting them in the function scope as well that keeps the usual legacy usages working. – Bergi Nov 18 '16 at 14:00