JavaScript basics: Hoisting, part one => variables

Tiety Kooistra
10-12-2016

When you start learning JavaScript you frequently hear two advices. Firstly: declare all your variables at the top of your file, or as high as possible. Secondly: Don’t call a function before you have written it. These are two best practices, but why? Lets take a look at the JavaScript concept of hoisting to figure this out.

What is hoisting?

function and variable declarations are hoisted/moved to the top of the scope in JavaScript.

This means that when you run your JavaScript code it is quickly read once to find all declarations. The declarations are then placed at the top of the function or global scope. Only after that the code gets executed from top to bottom. This means your code gets reordered before it is executed possibly giving a completely different result then you expect.

Lets take a look at some examples to find out what the rules are that JavaScript uses for hoisting.

First: hoisting only happens with function and variable declarations. What are those?

Variable declarations

var x;
// Variable declaration and initialization

x = "Hello World";
// Variable assignment

// Or all in one
var y = "Hello World";

Now that we know what variable declarations are, why does it matter. Lets look at some examples.

function sayHello(){
    var hello = "Hello World";
    return hello;
}

console.log(hello); 
//ReferenceError: hello is not defined

Why does this happen? JavaScript has scope. Because the variable is declared inside the sayHallo function scope, it is only available to that function and its children. The console.log is called in the global scope and can’t search inside of the sayHello function scope. Therefor we get the referenceError back in this case stating hello is not defined.

What about this example?

function sayHello(){
    hello = "Hello World";
    return hello;
}

console.log(hello); 
// returns "Hello World"

This does work. The key is the missing ‘var’ statement before hello. JavaScript is smart enough to see that you are declaring a value to a name, assigning a variable. But a variable with that name does not exist on the function scope and neither on the global scope yet. The variable is then made and put on the global scope. Here we touch on an other best practice, always explicitly declare your variables with the ‘var’ keyword before it. Later more about the new ‘let’ and ‘const’ keywords in ES2015/ES6.

Variables and hoisting.

So what happens with variables when hoisting is playing a role? For example when a variable is used before is has a value assigned to it, or if you try to use a variable that does not exist at all.

(function() {
    var one = 1;
    var two = 2;
    var three = 3;
    alert(one + " " + two + " " + three); 
    // alerts 1 2 3
})();

(function() {
    var one = 1;
    alert(one + " " + two + " " + three); 
    // alerts 1 undefined undefined
    var two = 2;
    var three = 3;
})();

(function() {
    var one = 1;
    alert(one + " " + two + " " + three); 
    // Uncaught ReferenceError: three is not defined
    var two = 2;
})();

Lets take a look at how JavaScript interprets this code, how does it look after the hoisting process is finished?

(function() {
    var one;
    var two;
    var three; 
    // the declared variables are hoisted to the top of the scope
    one = 1;
    two = 2;
    three = 3; 
    // only now the variables are assigned their values
    alert(one + " " + two + " " + three); 
    // alerts 1 2 3
})();

(function() {
    var one;
    var two;
    var three;
    one = 1;
    alert(one + " " + two + " " + three); 
    // alerts 1 undefined undefined
    two = 2;
    three = 3; 
    // the value assignment of variables two and three 
    // is happening after the alert
})();

(function() {
    var one;
    var two;
    one = 1;
    alert(one + " " + two + " " + three); 
    // Uncaught ReferenceError: three is not defined
    two = 2;
})();

ES2015 ‘let’ and ‘const’

Let’ and ‘const’ are similar to ‘var’ but instead of function scoped, they are block scoped. This means that ‘let’ and ‘const’ are scoped to a code block, recognizable for the curly braces around it ‘{ }’, for example with if statements or loops.

let x; 
// Declaration

x = "Hello World"; 
// Initialization and Assignment

// Or all in one
let y = "Hello World";

const z = "Hello World"; 
// only can be declared initialized and assigned at once

Note the difference here with a ‘var’. A ‘let’ is only declared but not initiated yet in the first line of code. This means JavaScript will trow an error if you try to use the ‘let’ before it is initialized. A ‘let’ is in the so called Temporal Dead Zone from the point you declare it tot when it is initialized and assigned a value. With a ‘const’ you need to declare, initialize and assign a value all at once. You are not aloud to first only declare it. This makes sense while with a const you cant change the value, making it constant. For the purpose of hoisting ‘const’ does not bring extra complications, ‘let’ does, lets get out a code example to see what those complications entail.

(function() {
    x;
    console.log(x); 
    // what will be logged here?
    y;
    console.log(y); 
    // and here?

    var x = "local";
    let y = "local";
}());

After hoisting the code looks like this.

(function() {
    var x;
    let y;

    x; 
    // still undefined
    console.log(x); 
    // undefined

    y; 
    // still only declared not initialized
    console.log(y); 
    // ReferenceError y is not defined => temporal dead zone

    x = "local";
    y = "local";
}());

Block scoped

What does block scoped mean exactly, lets take a look at the next example to clear things up.

var a = 1;
var b = 2;

if (a === 1) {
    var a = 11;
    let b = 22;
    console.log(a); // what will be logged here?
    console.log(b); // and here?
}

console.log(a); // and what about outside of the if?
console.log(b); // and for b?

The if statement ‘var a’ overwrites the global scope ‘a’ of line one with the value 11. The ‘let b’ is block scoped and therefor only available in code block of the if statement and is logging 22. The last two console.logs return 11 and 2. The global variable ‘a’  was overwritten and thus logs 11. ‘Var b’ is still the same and logs 2. ‘Let’ and ‘var’ are different things, they wont overwrite each other.

var a = 1;
var b = 2;

if (a === 1) {
    var a = 11; 
    // overwriting var a from line one
    let b = 22;
    console.log(a); 
    // logs 11
    console.log(b); 
    // logs 22 from the blocked scoped let b
}

console.log(a); 
// logs 11
console.log(b); 
// logs 2

So what about hoisting in JavaScript with function declarations? In my next post I will dive into their behavior with hoisting. JavaScript basics: hoisting, part two => functions.

LEAVE A REPLY

you might also like