var, let and const

Introduced in ES2015 let and const are additional ways to declare variables, alongside the traditional var keyword.

When variables are created with var they are either function scoped, when created inside of a function, or globally scoped if created outside of a function, at which point they are available as global variables.

Hoisting

All variable var declarations are processed before any code is executed, in a behaviour that is known as ‘hoisting’, so the variable declaration behaves as if moved to the top of the function, or the global code. Note that this hoisting only applies to the declaration, not the initialisation - the variable can be used before declaration/initialisation step occurs, but it will be populated as undefined. If you redeclare a var variable, it will not lose its value, ie:

var test = 1;
test = 2;
var test;
test; // 2

As effectively both var declarations are hoisted to the top of the function or global scope.

Because of this hoisting, it is always best practice to declare variables at the top of their scope

Accidental globals

A common problem with var declared variables is that they can leak into the global scope, causing unexpected problems. Mistakingly assuming a variable is local when declared within an if block for instance:

if (true) {
   var test = 'oops, a global variable'
}
console.log(test); // oops, a global variable

Or when using loops:

for (var x = 1; x < 10 ; x++) {
   console.log(x);
}

In this case, x is now on the global scope, if you then have another section of code using x this could cause unexpected problems when the variable has already been changed by your for loop.

For loops and re-using variables

Another common problem is reusing a variable in a for loop:

for (var x = 0; x < 10 ; x++) {

   setTimeout(() => console.log(x), 0)

}

This will output ‘10’ ten times - because the x variable is only getting created once, so each setTimeout callback is pointing to the same x variable, which will be set to 10 at the time the for loop exits.

let and const

The let and const keywords introduced in ES2015, give us variable declarations with block scope, so they will not leak from a block of code into the global scope. In addition, both let and const declarations at the top level of the code do not create a property on the global object:

var test1 = 'test'

let test2 = 'test2'

window.test1
 // "test"

window.test2 //
undefined

let

The let keyword allows you to declare a block scoped local variable, which can be re-assigned (but not redeclared). When used in a for loop, the let variable will actually rebind the variable for each iteration of the loop, so the above for…setTimeout code can be changed to a let variable:

for (let x = 0; x < 10 ; x++) {

   setTimeout(() => console.log(x), 0)

}

And now this will output the count from 0 to 10.

const

The const keyword allows you to declare a block scoped local variable, which cannot be re-assigned or redeclared. Its value must be assigned at the declaration point.

A key point to stress here is that a const variable creates a read-only reference to a value, it is not a constant value:

const test = [];
test.push('x'); // adds x to array, no errors
test = []; // Uncaught TypeError: Assignment to constant variable.

Temporal Deadzone and Hoisting

Unlike var, both const and let cannot be used before they are declared, however, they do have some hoisting behaviour. The period between the start of the current scope, and the let/const variable being declared is known as the Temporal Deadzone, a good description of this can be found at JS Rocks which has the following example:

let x = 'outer scope';
(function() {
    console.log(x);
    let x = 'inner scope';
}());

This throws an Uncaught ReferenceError: x is not defined error, as the inner x has been hoisted, but is now being referenced before it is declared, causing the error.

This behaviour is actually useful for finding bugs, as it forces you to think about where you are declaring and using your variables.

Which to use

I would recommend always using const where a variable doesn’t need to be reassigned, otherwise use let. However, when working in a REPL (Read, Eval, Print Loop) environment for testing, it’s probably better to use var as it lets you redeclare variables.

Written on May 21, 2018