3

I understanding how hoisting in javascript occurs, functions are hoisted before variables, and only the declarations are hoisted. But When I came across hoisting inside if/else conditionals, like this one:

foo(); // "b is the output"
var a = true;

if (a) {
  function foo() { console.log("a"); }
}
else {
  function foo() { console.log("b"); }
}

Now the conditional is true, so according to the if block, a should have been the output, but due to some kind of hoisting I assume b is the output.

So how is b the output?

hexacyanide
  • 88,222
  • 31
  • 159
  • 162
Hrishikesh
  • 335
  • 1
  • 13
  • Something to consider: at the time you call foo() the if hasn't been executed yet so the if condition hasn't been evaluated yet. (And variable a hasn't been assigned a value yet either.) – nnnnnn Feb 07 '16 at 07:04
  • @nnnnnn it makes no difference in the log, it still shows _b_ , When i use immediately invoked functions it does work as expected. – Hrishikesh Feb 07 '16 at 07:11
  • 1
    Try running this function in 2018 & the foo() will output `a` due to some weird JS parts – Rahul Gupta Mar 09 '18 at 06:49

4 Answers4

2

In JavaScript, variables, function expressions and function declarations are hoisted to the top of the scope.

Function declarations defines a named function variable without requiring variable assignment.

And important to know is that the entire body of the function declaration gets hoisted up the scope.

E.g.

function outerFunction() {
    console.log(typeof functionDeclaration); // outputs "function"

    function functionDeclaration() {
        // ... function body
    }
}

This is because, because of hoisting the code runs like so:

function outerFunction() {
    function functionDeclaration() {
        // ... function body
    }

    console.log(typeof functionDeclaration); // outputs "function"
}

In your case, the last function declaration for foo is hoisted to the top of the scope overriding all the other function declarations. Therefore, it logs "b".

Variables and function expressions, however, get hoisted without their assigned values.

E.g.

function outerFunction() {
    console.log(functionExpression); // outputs "undefined"      

    var functionExpression = function () {
        // ... function body
    }
}

Runs more like so,

function outerFunction() {
    var functionExpression = undefined;

    console.log(functionExpression); // outputs "undefined"

    functionExpression = function () {
        // ... function body
    }
}
Prashant
  • 7,340
  • 2
  • 25
  • 24
  • Thanks, so this means; 1st the foo() with **a** will get hoisted and and then foo() with **b** will get hoisted, therefore the latter will be considered and the JS engine has only one reference to a function *foo* so it will execute it which has *console.log('b')* Am I right? – Hrishikesh Feb 07 '16 at 07:15
  • function expressions are not hoisted, i think that may cause some confusion since you're saying that variable are hoisted but not initializing with the function expression value – Thiago Dias Aug 18 '19 at 13:25
  • Function expressions are [NOT](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting#function_and_class_expression_hoisting) hoisted. – Danil Zinchenko Oct 08 '22 at 16:16
1

(Ignoring the slightly dodgy behaviour that certain old browsers may have had:)

In Javascript, function statements are scoped within the containing function (or globally if there is no containing function), they're not scoped within an if or else or loop block. So you can't declare a function conditionally in that manner (it can be done another way; see below). And if you declare more than one function with the same name in the same scope the later one will overwrite the first one.

So what happens with your code is:

  1. Both function statements are hoisted, but

  2. They both have the same name so the first is overwritten by the second.

  3. The variable, a is created but not yet assigned a value.

  4. The foo() statement is executed, logging "b"

  5. a is assigned the value true.

  6. The if is executed. The condition is true, but neither the if nor else branches actually do anything because they don't contain statements other than the function declarations that were hoisted earlier.

If you want to create functions conditionally you have to declare a variable and then assign a function expression to it. And then you can not call the function until after that assignment:

var foo;
var a = true;

if(a)
    foo = function() { console.log("a"); };
else
    foo = function() { console.log("b"); };

foo();
nnnnnn
  • 147,572
  • 30
  • 200
  • 241
  • You're welcome. Note that `var` doesn't have block scope either, it has function scope, but the newer variable declaration keywords `let` and `const` *do* have block scope. – nnnnnn Feb 07 '16 at 07:35
  • @nnnnnn how about the result of this program? – Chao Mar 14 '18 at 20:47
  • how do you explain this snippet? ``` while(x < 2000) { if(x % 2 == 0) function aa(i) { return i + 2} else function aa(i) { return i * 2 } x = aa(x); console.log(x + ', ') }``` – Chao Mar 14 '18 at 20:48
  • @Chao - I don't, other than to make a guess (that I don't have time to research now) that browser behaviour has changed since two years ago when I wrote that answer. Both your snippet (assuming `x` is initialised first) and the OP's code still have the behaviour I described in my answer if I run them in IE, whereas Chrome and FF now seem to conditionally redefine the function based on the `if` condition. I *think* the first sentence of my answer was referring to old versions of IE at the time, but I can't remember now. – nnnnnn Mar 16 '18 at 00:19
  • @Chao - P.S. Note that your snippet doesn't run at all in strict mode. – nnnnnn Mar 16 '18 at 00:19
  • If this is the case, how come if I try to run `foo(); if (false) { function foo() {} }`, I get `Uncaught TypeError: foo is not a function`? (I only tried this in the Chrome devtools console, so maybe that's not the normal result.) – M Miller Jan 16 '20 at 01:21
  • 3
    @MMiller - The ECMA spec has moved on, and current browsers don't treat this the same way older browsers did. See [this answer to a similar question](https://stackoverflow.com/a/10069457/615754) for more information about current behaviour. – nnnnnn Jan 16 '20 at 02:50
-1

The is not the correct behavior anymore in modern JS browsers! The modern browsers will compile and hoist the mentioned code like this:

// hoisting (treat foo declarations as a normal variable declaration inside if statement)
var a;
var foo;

// now the assignment
foo(); // TypeError (foo = undefined)

a = true;

if (a) {
  function foo() { console.log("a"); }
}
else {
  function foo() { console.log("b"); }
}
Budi Salah
  • 153
  • 2
  • 11
-2

This is a unspecified behavior, different browser behaves diffrently.

MDN explain

In chrome & firefox, it will output foo is not a function.

And in safari, it will output b.

another doc of MDN