function definitions not hoisted
Asked Answered
N

2

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

Naji answered 18/11, 2016 at 12:47 Comment(12)
How does the second foo() definition ever get executed? Surely true is always true...Neille
@Neille 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.Unrighteous
That code will cause errors in strict mode. Defining functions in conditional clauses like that is bad practice.Moten
I just gave an ex for hoisting. Just an exNaji
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!Neille
@Moten It doesn't throw exceptiOns here, but I agree that it's badGossamer
@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".Unrighteous
@Unrighteous But I tested it here, it doesn't throw this exception, or my browser is different than ur?Gossamer
Related doc: ecma-international.org/ecma-262/5.1/#sec-10.5Bookshelf
@FREEZE I'm on Chrome v54. I tried it within the Fiddle, and it threw a console error as well.Unrighteous
@Unrighteous I've tried now and it worked, weird, I've now received the same exceptionGossamer
Why are you writing code like this?Argus
H
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).

Hyrax answered 18/11, 2016 at 13:24 Comment(3)
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.Naji
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.Naji
@VivekChandra It never was valid in the first place. In ES5 strict mode, it threw a syntax error. Function statements and similar things 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.Hyrax
B
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.

Breakfront answered 18/11, 2016 at 12:59 Comment(4)
You could also note that in strict mode it'd also result in this ReferenceError.Gossamer
@FREEZE Good catch, I added a comment.Breakfront
@Breakfront -- 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 changedNaji
@VivekChandra Maybe you will find something in the release notes or rather the change log.Breakfront
H
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).

Hyrax answered 18/11, 2016 at 13:24 Comment(3)
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.Naji
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.Naji
@VivekChandra It never was valid in the first place. In ES5 strict mode, it threw a syntax error. Function statements and similar things 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.Hyrax

© 2022 - 2024 — McMap. All rights reserved.