Understanding var, let and const in JavaScript

In this tutorial, we’ll explore the three different ways to declare a variable in JavaScript var, let  and const keywords. The keyword var is available since the beginning of JavaScript. Keywords let and const were added in ES6. We’ll also understand why var is problematic and we should use let or const instead.

var keyword

Till ES5 var was the only way to declare variables in JavaScript:

//Declaring variable
var car;
//Initializing variable
car = 'Tesla';

Most of the time we do the declaration and initialization at once:

var car = 'Tesla';

Hey, I know these simple things. They are common across all programming languages.

No, they are not so simple. In JavaScript scoping works differently and it causes great confusion and frustrations to beginners. There is also the concept of hoisting and shadowing.

If you have no idea of “scopes”, “hoisting” or “shadowing”, don’t panic and read further.

Scope of a variable in JavaScript

The term scope in the programming world refers to the visibility of a variable or other entities. Variables are not visible outside the scope in which they are declared. In JavaScript (till ES5), the scope of a variable is either a global scope or a function scope. Before the ES6, there was no concept of block scope.

Global Scope

Any variable declared outside of a function is of global-scope, which means they can be accessed by any part of the program.

Let us see things in action:

var msg = 'Hello World';
function greet(){
  console.log(msg);
}

greet();

Output:

Hello World

In this example variable msg is defined outside of any function. So it has a global scope. We know that if any variable has a global scope, any part of the program can access it. Hence it is visible inside the function greet().

Local Scope (Function Scope)

If a variable is declared inside a function, it is NOT visible outside that function. Even if the variable is declared (using var) inside a code block (e.g,for, while, if), the variable is visible throughout the whole function, outside that code-block.

Let us take an example:

 function greet(){
  if(true){
    var msg = 'Hello World';
  }
  console.log(msg);
}

greet();
console.log(msg);

Output:

Hello World
Uncaught ReferenceError: msg is not defined

As we can see the scope of the variable msg is function scope. Although the variable is declared inside the if block, it is available outside the local-block within the function. But it is not available outside the function as we can see the output Uncaught ReferenceError: msg is not defined for trying to access it outside the function.

It happens due to hoisting.

Hoisting

Where do you hoist a flag?

On the top of the castle.

Let us take an example to understand *hoisting* in JavaScript:

console.log(msg);
var msg = 'Hello World';

Output:

undefined

Wait…! I think we should get the following error because the variable msg was not declared when we tried to access it

Uncaught ReferenceError: msg is not defined

Well, in JavaScript, it does not matter where variables are declared within a particular scope. All variable declarations are moved to the top of their scope.

The above program was translated by the JS interpreter as:

var msg;
console.log(msg);
msg = 'Hello World';

As we can see the variable declaration was hoisted at the top of the scope, global-scope in this case. But the variable assignment was NOT hoisted. The JavaScript interpreter assigned it the default value undefined. Hence we got the output undefined.

The same concept would apply with function scope (local scope). All variable declarations inside a function will be hoisted at the top of the function, no matter where they are declared in the function.

 function greet(){
  if(true){
    var msg = 'Hello World';
  }
  console.log(msg);
}

greet();
console.log(msg);

The above program was translated by JavaScript interpreter as if var msg was hoisted at the top of the function although it is declared inside the if block.

 function greet(){
  var msg;
  if(true){
    msg = 'Hello World';
  }
  console.log(msg);
}

greet();
console.log(msg);

Hence we get the output – Hello World

Shadowing

If we declare a local variable and a global variable with the same name, the local variable will take precedence when it is referred inside a function. It is called shadowing, the inner variable shadows the outer variable. The Javascript interpreter searches for the variable at the innermost scope and continues until the first match is found.

var msg = 'Hello ';
var name = 'John';
function greet(){
  var msg = 'Julia';
  if(true){
    console.log(msg + name); //Hello Julia
  }
}

greet();

The moment the if block is executed by the JavaScript interpreter, it looks for the variable msg in the local scope. As it is found there, it uses the local scope variable, even if the same variable is declared in the outer scope as well. Variable name it is not found in the inner scope so the JS interpreter tries to find it in the outer scope and it is found.

Scoping and Hoisting works the same for functions as it works with variables.

Issues with var

No that we have understood different concepts related to var , let us discuss the issues with var and why you should avoid using it.

Let us take an example:

var greetings = 'Hello John';

function greet() {
  console.log(greetings); // undefined
  if (true) {
    var greetings = 'Hello Julia';
    console.log(greetings); //Hello Julia
  }
  console.log(greetings); //Hello Julia
}

greet();

If you are coming from another programming language background like Java, Python, C# etc, you must be thinking “what the heck”. Don’t you think as greetings is a global variable the first output should be Hello John ? And the third output should be Hello John because although inside the if block we have declared the same variable greetings it should not be visible outside the if block?

Incorrect!

We just learned about scoping, hoisting and shadowing in JavaScript. We know there is no concept of block scope in JavaScript(till ES5). Let us apply these learnings for the above example.

When the JavaScript interpreter encounters the variable declaration for greetings inside the local if block, it hoisted the variable declaration at the top of the function. But only the declaration is hoisted, not initialization. The variable has the default value undefined that the JavaScript interpreter assigns while declaration. When greetings is referred inside the first print statement, due to *shadowing* the JavaScript interpreter used the inner declaration of the greetings variable, not the global one. But since the inner declaration of greetings was not initialized yet, it printed its default value undefined.

Again, the same concept of hoisting and shadowing got applied in the third output. The interpreter took the function-scoped declaration of greetings. The only difference is this time greetings was already intialized inside the if block.

As we can see variables declared with var are confusing and prone to introduce bugs in the program, if not written with at most care. And that is the reason you should never use var to declare variables in your program. Always use let or const to declare variables.

let keyword in ES6

The ES6 specification addressed these issues with JavaScript and introduced **block scoping** with a new keyword let.

Let us take the same example as the previous one with a change – replacing var with let keyword :

let greetings = 'Hello John';

function greet() {
  console.log(greetings); //Hello John
  if (true) {
    let greetings = 'Hello Julia';
    console.log(greetings); //Hello Julia
  }
  console.log(greetings); //Hello John
}

greet();

As we see the output is more predictable with the let keyword. The first time the greetings is referred in the print statement, it took the global value because it was not yet declared locally. The JavaScript interpreter declared the second instance of greeting in the local scope inside the if block. It retains the local value Hello Julia inside the if block. But outside the if block in where the variable was declared, the same greetings variable is not visible to the interpreter. Hence it took the global declaration in the third print statement.

As we see the let keyword is behaving similar to most of the other programming languages and is having *block scope*. That is the reason we should “mostly” use let keyword to declare variables in JavaScript and never the var.

const keyword in ES6

I hope you have noticed the earlier statement – we should “mostlyuse let keyword. We have used mostly because when we have variables that are never going to change their value, we should declare them with const keyword. It is a good safety feature because if any part of the program try to change the value of the variable, the JavaScript interpreter will raise an Exception.

Let us see things in action:

function greet() {
  const greetings = 'Hello World';
  if (true) {
    greetings = 'Hello Julia';
    console.log(greetings);
  }
}

greet();

If we run this code, the JavaScript interpreter will raise an Exception where we try to change the value of const variable greetings.

Uncaught TypeError: Assignment to constant variable.
    at greet (<anonymous>:4:15)
    at <anonymous>:9:1

There is no other difference between let and const

Conclusion

We hope this tutorial gave you a good understanding of all the three ways to declare variables in JavaScript. We discussed as var does not have block scope, it is bug prone and we should never use it. We should use ES6’s let or const instead. We also learned that we should use const when we want to restrict a variable so that it never changes its value and remain a constant.

2 thoughts on “Understanding var, let and const in JavaScript”

  1. Nice tutorial I really love it… And please if you can explain with more examples on Abstraction please….. I understood your concept of abstraction but you know I haven’t really know it like know it

    Reply

Leave a Reply