featured image

Javascript (ES6) - var vs let vs const


Introduction

ECMAScript's sixth edition (ES6) or also known as ECMAScript 2015 introduced a lot of new features for us, developers. A few of them are let and const keywords. These new keywords is being used for declaring variables. Stop right there. We can already declare variables with the var keyword. Why another keywords for something we already have? Let's go on!

Scope

Let's discuss what a scope is first. So basically a scope defines the visibility of your variables, functions - everything that you declare.

So what does this mean in practice? Take a look at this example

var foo = 'bar';

function dogSound() {
  var dog = { name: 'Duke' };
  foo = dog.name + 'said: woof';
}

function catSound() {
  var cat = { name: 'Kitty' };
  foo = cat.name + ' said: meow';
}

console.log(foo);
dogSound();
console.log(foo);
catSound();
console.log(foo);
console.log(cat);
console.log(dog);

Output

bar
Duke said: woof
Kitty said: meow
ReferenceError: cat is not defined

So what happened? First we initialized a new variable called foo in the global function scope. Then we have two new variables in the dogSound and catSound function scope. So the best way to understand this is to visualize it. I'll help you do this using some trees.

Short explanation: box = function, blue = variable

Every function (box) can:

  • use variables (blue) declared in them
  • call functions declared in them (leaves)
  • use variables and functions declared in ancestors

Therefore dogSound can:

  • use its own variables: dog
  • use variables declared in ancestors: foo
  • use functions declared in ancestors: dogSound and catSound

This one is called function scope. Everything that you declare in a function is visible in the whole function.

Scope continued

Now let's see another example

var dog = { name: 'Duke', sound: 'woof' };
var cat = { name: 'Kitty', sound: 'meow' };

function animalMakeSound(animal, times) {
  for (var i = 0; i < times; i++) {
    console.log(animal.name + ' said: ' + animal.sound);
  }

  console.log(animal.name + ' said: "' + animal.sound + '". Exactly ' + times + ' times.');
}

animalMakeSound(dog, 2);
animalMakeSound(cat, 4); // Hungry cat

Output

Duke said: woof
Duke said: woof
Duke said: "woof". Exactly 2 times.
Kitty said: meow
Kitty said: meow
Kitty said: meow
Kitty said: meow
Kitty said: "meow". Exactly 4 times.

Let's see how this looks visualized

But now you should say: "Dave, this tree is wrong!" Well, that's what you should say if you understood what does the var do.

There you go, I fixed it. Okay, so we introduced a new shape in the diagram: a rounded square. Rounded square are blocks. So what is the block scope?

{                // {{ block scope
  doSomething(); // {{
}                // {{ 

while (true) {   // {{ block scope
  doSomething(); // {{
}                // {{

for (var i = 0; i < 10; i++) { // {{ block scope
    console.log(i);            // {{
}                              // {{

function func() {                // { function scope
  doSomething();                 // {
  for (var i = 0; i < 10; i++) { // {{ block scope
    console.log(i);              // {{
  }                              // {{
}                                // {

So what happens when we use var inside a block scope? var declares variables in the function scope. So it will declare the variable in the function we are currently in.

function animalMakeSound(animal, times) {
  for (var i = 0; i < times; i++) {
    console.log(animal.name + ' said: ' + animal.sound);
  }

  console.log(animal.name + ' said: "' + animal.sound + '". Exactly ' + times + ' times.');
}

This function is actually interpreted like this

function animalMakeSound(animal, times) {
  var i;

  for (i = 0; i < times; i++) {
    console.log(animal.name + ' said: ' + animal.sound);
  }

  console.log(animal.name + ' said: "' + animal.sound + '". Exactly ' + times + ' times.');
}

That means, if we replace this line

- console.log(animal.name + ' said: "' + animal.sound + '". Exactly ' + times + ' times.');
+ console.log(animal.name + ' said: "' + animal.sound + '". Foor loop ran ' + i + ' times.');

Then the output doesn't change. The i variable was declared with var so it is in the function scope rather than the for's block scope.

let myScope be a block

ES6 to the rescue! By now you - hopefully - understand the difference between function and block scope. So what does the let keyword do?

Let's use the last example to demonstrate it

function animalMakeSound(animal, times) {
  for (let i = 0; i < times; i++) {
    console.log(animal.name + ' said: ' + animal.sound);
  }

  console.log(animal.name + ' said: "' + animal.sound + '". Foor loop ran ' + i + ' times.');
}

Output

Duke said: woof
Duke said: woof
Duke said: woof
Duke said: woof
Duke said: woof
ReferenceError: i is not defined

Oops, there it goes, an ERROR! We tried to access a variable which was declared in the for's block scope!

animalMakeSound only has access to animal and times but not i.

const

ES6 introduced several new keywords, one of them is const. If you read the rest of the post then you will easily understand what this keyword does.

const VERSION = "1.0.0";

console.log('Version: ' + VERSION);
VERSION = "1.1.0";

Output

Version: 1.0.0
TypeError: Assignment to constant variable.

You see? It's simple. const declares a constant variable. You can't reassign its value. const declares in the block scope, same as let.

Which one to use?

Basically you must never use var. It doesn't have any pros over let and const. Whenever I see var in >= ES6 codebase, I instantly know that code smells. Whenever you need a variable, declare it with let. Whenever you need a constant, declare it with const.

Recap

function scope

function myFunc() {  // {
                     // { function scope
}                    // {

block scope

{                  // {
  console.log(5);  // { block scope
}                  // {

for (let i = 0; i < 10; i++) {  // {
  console.log(i);               // { block scope
}                               // {

while (true) {                    // {
  console.log('Infinite loop');   // { block scope
}                                 // {

var

  • Declares in the function scope
  • Pros: Nothing
  • Cons: Code smell

let

  • Declares in the block scope
  • Must be preferred over var when you need a variable

const

  • Declares in the block scope
  • Can't reassign value
  • Must be preferred over let and var when you need a constant

Outro

Thank you for reading! I hope you finally understood what is a function scope, block scope and the difference between var, let and const! Check out my other posts if you liked this one!