Declaring a Winner 🥇 Between JavaScript's var, let and const

When ECMAScript 6 (also known as ECMAScript 2015) was released a collection of new APIs, programming patterns and language changes became a standard. Since ES6 started gaining browser and nodejs support developers are wondering if they should stop using the traditional var to declare variables.Confused declaring var, let and const?

ES6 introduced two new ways to declare variables, let and const.

  • var - has function level scoping and can change the value reference
  • let - has block level scoping and can change the value reference
  • const - has block level scoping but cannot change the value reference

Both provide better block scoping that var. const differs from let because the immediate value cannot be changed once it is declared.

Variables declared using var are function scoped, which has led to confusion to many developers as they start using in JavaScript.

Are You Read for the Great JavaScript Battle of var vs let vs const?

Variables declared using var can be done anywhere in a function. But instead of being limited to block, like a for loop, in the function the variables are hoisted to the top of the function.

This is why the best practice is to declare all the variables used in a function at the beginning of the function.

Declaring variables at the top make it clear to all developers what functions are available. It also reduces the chance a programmer might think a variable's scope is limited to a child block.

Even if a variable is declared within a block the JavaScript interpreter will 'hoist' the declaration to the top of the function. Since this is done by the interpreter you don't clearly see this while debugging the code.

var Declarations and Hoisting

In this example I declare the index variable in the for loop declaration.

var len = 10;

for(var index = 0; index < len; index++){

    //do something
    console.log(index);

}

console.log(index);

This canonical demonstration of JavaScript variable declaration gives a little insight into the scoping issue.

The script outputs the following:

0
1
2
3
4
5
6
7
8
9
10

The value 10 comes outside the for loop's scope, which means the variable is available anywhere in the function.

var len = 10;

for(var index = 0; index < len; index++){

    var inside = index;
    //do something
    console.log(inside);

}

console.log(inside);

Let's repeat the example, but time I will declare a variable inside the block so it is more obvious.

This time the output is similar, but 9 is repeated because the block code does not execute the 10th time.

0
1
2
3
4
5
6
7
8
9
9

So far the examples have been rather harmless examples. This time I am going to switch to the array forEach function to loop over the values.

var list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

list.forEach(function(value, index, src){

    console.log(value);

});

console.log(value);

This time the output is a bit different:

1
2
3
4
5
6
7
8
9
10
Uncaught ReferenceError: value is not defined

value is an exception in the last one because it was declared as part of the forEach callback function, not part of a block within a function. So its scope is limited to the forEach function.

Now let's declare value outside the forEach function.

var value = "foo",
    list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

list.forEach(function(value, index, src){

    console.log(value);

});

console.log(value);

Again the output is different because the variable is declared outside the inner function. I even made it a string so you could clearly see where the value was declared.

1
2
3
4
5
6
7
8
9
10
"foo"

value is clearly two different variables, despite sharing the same name. Their scope is different.

How let Works

let and var are similar in that you can declare a variable and its value. Then later you can change the value.

But they differ in how scope is applied. var uses function level scope, and let uses block level scope.

The first code example demonstrates this principle:

let len = 10;

for(let index = 0; index < len; index++){

    //do something
    console.log(index);

}

console.log(index);

This time we get the index value as the loop executes. But accessing index outside of the for loop throws an exception:

0
1
2
3
4
5
6
7
8
9
Uncaught ReferenceError: index is not defined at....

This is because the index variable's scope is limited to the for block. It's declaration is not available outside the block.

Attempting the second example should make this point even clearer.

let len = 10;

for(let index = 0; index < len; index++){

    let inside = index;

    //do something
    console.log(inside);

}

console.log(inside);

The loop executes but a ReferenceError exception is thrown:

0
1
2
3
4
5
6
7
8
9
Uncaught ReferenceError: inside is not defined at....

The variable inside is declared within the for loop. If it were declared using var the final console.log would have echoed 9 instead of throwing a ReferenceError exception.

The third demo triggers an exception by the interpreter because the value variable is declared outside the forEach function and is still within scope.

let value = "foo",
list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

list.forEach(function(value, index, src){

    console.log(value);

});

console.log(value);

This time each value in the array is logged, plus the 'foo' value set outside the scope of the forEach block:

1
2
3
4
5
6
7
8
9
10
foo

Let variables are limited to the scope of their containing block, which means they cannot be hoisted. It does not mean they cannot be accessed by a child block.

In the final let demo I am declaring the inside variable outside the for loop scope. But as you will see the variable is available to modify within the loop.

let inside = "foo", 
list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

list.forEach(function(value, index, src){

    inside = inside + value;
    console.log(value);

});

console.log(inside);

The array values are echoed out as expected:

1
2
3
4
5
6
7
8
9
10

But when we write the final inside value you see:

foo12345678910

This of course is the initial value plus each one of the array values appended to the end of the string!

As you can see there are many differences with variable declaration between var and let.

We still have the const declaration to consider, which of course changes things once more.

How const Works

const scopes the same as let. The difference is one a const value is set it cannot be changed.

const len = 10;

for(const index = 0; index < len; index++){

    //do something
    console.log(index);

}

console.log(index);

This time we get the initial value, 0, but as soon as the for loop attempts to change the value an exception is thrown:

0
Uncaught TypeError: Assignment to constant variable.

I wont use the second example here because there is nothing new to learn.

However, the third example, where we declare value outside the forEach function produces something you may not have expected.

const value = "foo",
    list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

list.forEach(function(value, index, src){

    console.log(value);

});

console.log(value);

1
2
3
4
5
6
7
8
9
10
foo

The scope of value does not pass through to the forEach callback function. Instead the value parameter is a different variable, just like using var.

Now lets see what happens when we try to modify a const variable inside a forEach method:

const inside = "foo",
    list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

list.forEach(function(value, index, src){

    inside = inside + value;
    console.log(value);

});

console.log(inside);

Another Assignment exception.

Uncaught TypeError: Assignment to constant variable.

This time a new variable, innerValue is declared within the forEach method. As it is declared we set a value using the current value.

const list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

list.forEach(function(value, index, src){

    const innerValue = "foo-" + value;
    console.log(innerValue);

});

This time everything works!

Each time through the function is a new execution of the forEach function, so it is a new block or scope. Any previous innerValue declaration is not within the code of this iteration.

foo-1
foo-2
foo-3
foo-4
foo-5
foo-6
foo-7
foo-8
foo-9
foo-10

Are you ready for an unexpected twist to the const rules?

I am!!!!

This time I will set inside to an object. Inside the forEach function I will update the foo property.

const inside = {"foo": "bar"}, list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

list.forEach(function(value, index, src){

inside.foo = inside.foo + value;
console.log(value);

});

console.log(inside);

Are you expecting another exception?

I was!

Instead everything runs perfect.

The final value of inside.foo is:

{foo: "bar12345678910"}

This because the const declaration sets an immutable reference to a value, which I assume is similar to a pointer.

When setting a const variable to an object you are still free to modify their properties.

This is true unless those properties are declared as a const variable.

Should you even use var in an ES6+ World?

If you are using ES6+ syntax then no, not really.

The let and const declarations provide better scope management than the traditional var. Plus the var keyword may confuse programmers coming from other languages like VB or Java that use var to declare variables, but with different rules.

But I do not advise doing a global search and replace for your var declarations because I bet you will see many scope related exceptions pop-up!

Instead live with var as much as you can. Maybe you update a function when you are working doing other updates.

Maybe you dedicate a day or more to converting your entire code base. This is up to you.

Remember your existing code using var will continue to work, so maybe you just let it keep working and don't risk breaking anything.

My Advice

If you are writing code that will executed a controlled environment where you know let and const are supported, don't use var.

The best place for this is nodejs modules. If you use nodejs 6.5 and above your environment supports ES6, thus the let and const keywords.

If you are writing for an enterprise that has deprecated Internet Explorer for Edge and other modern browsers then go ahead and use ES6 syntax.

If your code might run on Internet Explorer or older Android phones you may want to stick to the ES5 syntax and only use var to declare variables.

Of course if you use TypeScript or Bable you can have them transpile your code to ES5. I am not a huge fan of this approach as I think the code gets a little funky to debug.

What ever you choose make sure your team is on board and will follow your coding patterns.

Don't feel like you need to stop everything and update existing code today. It will still work. Update it as time permits, but I don't advise making it a priority.

Share This Article With Your Friends!

Googles Ads Facebook Pixel Bing Pixel LinkedIn Pixel