9

I'm reading the second book of the series "You don't know JS" and I've read that functions are hoisted before variables.

So this is the code:

foo(); // 1

var foo;

function foo() {
    console.log( 1 );
}

foo = function() {
    console.log( 2 );
};

The output of this will be 1. But why? Functions are hoisted first and then variables. So after my function foo (the one that prints 1) is hoisted it has to be followed by the variable foo. So the result should be "undefined" instead of "1".

I expect the code to behave as if it had been:

// hoisted first
function foo() {
    console.log( 1 );
}

// hoisted second
var foo;  // implicitly initialized to 'undefined'

foo();  // call 'undefined' - error?

foo = function() {
    console.log( 2 );
};

What's happening here?

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
BoSsYyY
  • 563
  • 5
  • 13

1 Answers1

9

As said, functions are hoisted before variables; if the interpterer comes across var foo after foo has already been defined in the scope, it will simply be ignored. It doesn't assign foo to undefined, it only ensures that a variable named foo exists in the current scope - which it already does.

As it says in your You Don't Know JS link:

multiple/duplicate var declarations are effectively ignored

Here's another commonly seen example of hoisted duplicate variables being ignored that might be more familiar/intuitive:

if (false)
  var foo = false;
else
  var foo = true;

turns into

var foo;
var foo; // does not actually do anything; does not assign `undefined` to `foo`, ignored
// foo just *happens* to not have anything assigned to it in almost all cases like this
if (false)
  foo = false;
else
  foo = true;
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • But even if I set the variable to a function expression I will again get the result 1. – BoSsYyY May 26 '18 at 07:48
  • @BoSsYyY You already set the variable to a function expression in your original code. What do you mean? – melpomene May 26 '18 at 07:49
  • 2
    @BoSsYyY, that *assignment* is not hoisted: it happens after you have called the function. – trincot May 26 '18 at 07:49
  • Yes I know that assignment is not hosted. But look at this example: foo(); // 1 var foo = function() { console.log("2"); }; function foo() { console.log( 1 ); } here variable "foo" will be hoisted then the function expression should be assigned to it (but it isn't) and then exectuted. But the result is still 1 – BoSsYyY May 26 '18 at 07:53
  • 2
    @BoSsYyY Function declarations are hoisted; function expressions (with `=`) are not. That *is* assignment, so calling `foo` before the assignment results in `1`. – CertainPerformance May 26 '18 at 07:54
  • Oh yes. Got it. the assignment is going to happen after the function call. – BoSsYyY May 26 '18 at 07:55