Variable hoisting in javascript

(Note: I’ve added a brief discussion of ES2015’s let and const at the end of this answer.)

Fundamentally, what variable hoisting means is that no matter where you see var in any given scope, it’s as though it were at the very beginning of the scope. So these are all identical:

function foo() {
    var a = 42;
}

function foo() {
    var a;
    a = 42;
}

function foo() {
    a = 42;
    var a;
}

function foo() {
    var a;
    a = 42;
    var a;
}

They’re processed by the JavaScript engine as though they were:

function foo() {
    var a;
    a = 42;
}

Here’s an example actually using variable hoisting, and also giving an example of what I call The Horror of Implicit Globals (that’s a post on my anemic little blog):

function foo() {
    a = 42;
    b = 67;

    console.log(a); // 42
    console.log(b); // 67

    var a;
}
foo();
console.log(typeof a); // undefined
console.log(typeof b); // number?!
console.log(b);        // 67?!

Why does b exist outside of foo? Because inside foo, these two lines do very different things:

a = 42;
b = 67;

The first line sets the local variable a, because we declared it. Yes, we declared it later, but we declared it.

The second line creates an implicit global variable b, because we never declared b anywhere in foo.

More (on my blog):


ES2015 (aka “ES6”) introduced let and const. They’re handled slightly differently from var:

  1. They have block scope rather than function or global scope.
  2. The declaration is hoisted to the top of the block, but they don’t get any default value at that point; they get initialized (with undefined or the value you provide) only when the declaration is reached in the step-by-step execution of the code.

Demonstrating point #1 (block scope):

function foo() {
    {
        let a = 1;
        console.log(a); // 1
    }
    console.log(a); // ReferenceError: a is not defined
}
foo();

Demonstrating point #2: This would work with var, it doesn’t work with let:

function foo() {
    a = 42; // ReferenceError: a is not defined
    let a;
}
foo();

The time between when the identifier is reserved (declaration) and when you can use it (initialization) is called the Temporal Dead Zone within which you can’t use the variable.

Leave a Comment