DEV Community

Cover image for var, let or const: what's the real difference?
Pippa Thompson
Pippa Thompson

Posted on

var, let or const: what's the real difference?

The var, let and const keywords

At this point, the let and const keywords are pretty much ubiquitous. Before their existence, we could only use the var keyword to declare variables which store our data. Although you will still encounter var in JavaScript code today, let and const bring with them some features which help address certain issues you might encounter when using var.

Scope

One of the differences between let/ const and var relates to how they handle scope.

Scope refers to the accessibility of variables and functions in different parts of a program. Being a lexically scoped language, the accessibility of variables and functions in JavaScript depends on where they are declared in the program. Both let and const are block scoped whereas var is not. This means that variables declared with let and const within if/switch statements, for/while loops and curly braces { } are not accessible outside of this block.

Let’s see how this block-scoped behaviour affects our code:


for (var i = 0; i < 3; i++) {
  console.log(i)
}

console.log(`var i = ${i}`)


for (let j = 0; j < 3 ; j++) {
  console.log(j)
}

console.log(`var j = ${j}`)

// OUTPUT -->
// (first for loop - using var)
// 0
// 1
// 2
// "var i = 3"
// (second for loop - using let)
// 0
// 1
// 2
// "error"
// "ReferenceError: j is not defined"

Enter fullscreen mode Exit fullscreen mode

In the above example we have two for loops. In the first loop, we use the var keyword to declare variable i. Despite the fact that i is within a for loop, we are still able to access it outside the for loop.
In the second loop we use the let keyword to declare variable j . We try to access j outside of the for loop but we receive the ReferenceError: j is not defined. As it was declared using let, it is block scoped and therefore only accessible within the loop.

This same behaviour occurs when using the const keyword to declare variables within block scopes:


for (let i = 0; i < 3; i++) {
  const myName = 'Pippa';
  console.log(`${myName} -> ${i}`)
}

console.log(myName);

// OUTPUT -->
// (console.logging within the for loop)
// "Pippa -> 0"
// "Pippa -> 1"
// "Pippa -> 2"
// (console.logging outside the for loop)
// "error"
// "ReferenceError: myName is not defined"

Enter fullscreen mode Exit fullscreen mode

Here we have declared the variable myName using const inside a for loop. Although we can access myName inside the for loop as demonstrated by the console.log(${myName} -> ${i}), we cannot access it from outside the loop and we receive a ReferenceError when we try.

Redeclaration and Reassignment

Another way in which let / const differ from var relates to whether or not they can be redeclared or their values reassigned. Variables declared with var can be both redeclared and reassigned.


var myName = 'Pippa';

console.log(myName);

var myName = 'Philippa';

console.log(myName);

myName = 'Pippa Again'

console.log(myName)

// OUTPUT --> 
// "Pippa"
// "Philippa"
// "Pippa Again"

// No errors, we can declare myName, redeclare it and finally reassign the value.

Enter fullscreen mode Exit fullscreen mode

This can sometimes cause issues, especially when working with global variables. It’s one of the reasons that let and const were introduced because when using var you could end up accidentally redeclaring variables.

Let’s try the same using the let keyword:


let myName = 'Pippa';

console.log(myName);

let myName = 'Philippa';

console.log(myName);

// OUTPUT -->
/// "SyntaxError: Identifier 'myName' has already been declared"

Enter fullscreen mode Exit fullscreen mode

Here, we receive a syntax error informing us that the myName variable has already been declared. So with let, we cannot redeclare a variable. We can, however, reassign values:


let myName = 'Pippa';

console.log(myName);

myName = 'Philippa';

console.log(myName);

// OUTPUT -->
// "Pippa"
// "Philippa"

Enter fullscreen mode Exit fullscreen mode

No errors! Rather than redeclaring myName we just reassign its value, which is fine by let.

So how does const deal with redeclaration of variables? const, short for constant, displays the same behaviour as let when it comes to redeclaring variable, i.e. it does not allow variable redeclaration:


const myName = 'Pippa';

console.log(myName);

const myName = 'Philippa';

console.log(myName)

// OUTPUT -->
// "Pippa"
// "SyntaxError: Identifier 'myName' has already been declared

Enter fullscreen mode Exit fullscreen mode

The same as with let, we receive a SyntaxError when trying to redeclare a const variable. But what about reassigning the value of a const?


const myName = 'Pippa';

console.log(myName);

myName = 'Philippa';

console.log(myName)

// OUTPUT -->
// "Pippa"
// "error"
// "TypeError: Assignment to constant variable.

Enter fullscreen mode Exit fullscreen mode

That’s also a big no-no. This time we receive a TypeError telling us we have assigned a constant variable, which cannot be changed. As such, const is a good option to use for variables which you know won’t change.

While useful in some cases, doesn’t this behaviour seem a bit inflexible? Let me expand a bit more on the details of this. As we just saw, when using const to store primitive data types (Number, String, Boolean, null, undefined, BigInt and Symbol) we cannot redeclare or reassign the value. However, this is not the case when using const to store non-primitive data types (objects, which include arrays and functions).

When storing non-primitive data types, although we cannot redeclare them, we can mutate them. An example of this would be using const to store an object and then later adding on a new property or method.

The reason for this relates to how JavaScript stores different types of data. When we store an object (or any non-primitive data type) in a variable, for example const myObject = {...}, we are saving a reference to the object (essentially an “address” to a point in memory in the JavaScript memory heap) rather than storing the object itself. Primitive types in JavaScript are stored in a different way; the actual value is stored as opposed to a reference to a point in memory.

This means that we can use const to store an object, and later mutate this object as it is a non-primitive data type:


const carObject = {
  'make': 'Ford',
  'wheels': 4
}

console.log(carObject);

carObject.colour = 'red';

console.log(carObject);

// OUTPUT -->
/*
FIRST CONSOLE.LOG()
[object Object] {
  make: "Ford",
  wheels: 4
}
SECOND CONSOLE.LOG()
[object Object] {
  colour: "red",
  make: "Ford",
  wheels: 4
}
*/

Enter fullscreen mode Exit fullscreen mode

As you can see, we declared carObject using const and were able to add the colour property on later as the reference to the object which was saved in carObject has not been changed.

Hoisting

Another major difference between let/ const and var is their behaviour relating to hoisting.

Before we dive into these differences, let’s first discuss what hoisting actually is. To do so we must take a look at the 2 phases of the JavaScript execution context.

When a program is passed to a JavaScript engine there are 2 phases which will occur. JavaScript will first parse the file in what it known as the creation phase. During this phase, JavaScript will allocate memory for all variables and functions, amongst other processes such as creating the execution context and the scope chain.

Next comes the execution phase during which, as the name suggests, the JavaScript engine actually executes the code in the file.

Hoisting happens during the creation phase, and it is the process of JavaScript identifying any variables and functions in a file and “hoisting” them to the top of their respective scope so that they are available for use during the execution phase. However, as we have already discovered, not all variables are created equal, so how does the behaviour of let/const differ from var? Let’s take a look.

Var

Any variable that has been declared with var will be partially hoisted. The identifier name will be stored in memory but the value will be set to undefined until JavaScript reaches the assignment of the value during the execution phase.


console.log(myVar)

var myVar = 'I used to be undefined';

console.log(myVar)

// OUTPUT -->
// undefined | first console.log()
// "I used to be undefined" | second console.log()

Enter fullscreen mode Exit fullscreen mode

No errors were thrown, but the value of myVar was set to undefined until the execution phase during which JavaScript assigns the value as the string ‘I used to be undefined’.

Let and Const

Variables declared with let and const will be hoisted, but their values will be set to uninitialised. This is different from var as any values declared by var will initially be stored as undefined until the execution phase.

Let’s take a closer look at using let:


console.log(myLet)

let myLet = 'I used to be uninitialised';

console.log(myLet);

// OUTPUT --> 
// "error" 
// "ReferenceError: Cannot access 'myLet' before initialization

Enter fullscreen mode Exit fullscreen mode

The reference error message we received confirms this; Cannot access 'myLet' before intialization. So, JavaScript is aware of its existence, but it has not yet been initialised with any value. Practically speaking, what this means is that you should declare and assign the value when using let before you try and access it in your code - unless you want to receive an error that is!

That being said, some potentially unexpected behaviour occurs when we:

  • declare a variable with let but don’t assign it a value
  • access that variable
  • then assign it a value

That sounds a bit confusing, so let me show you what I mean with an example:


let partiallyHoisted;
// declare a variable using let but don't manually assign it a value

console.log(partiallyHoisted);
// access the variable 

partiallyHoisted = 'I used to be undefined';
// assign a value to the variable

console.log(partiallyHoisted)

// OUTPUT -->
// undefined | first console.log()
// "I used to be undefined" | second console.log() after assigning the variable a value

Enter fullscreen mode Exit fullscreen mode

No errors! In this case, although we tried to access partiallyHoisted before we assigned it a value, as we had declared it (and therefore JavaScript “knew” it existed) it is set to undefined by default, until we manually assign it a value.

This behaviour differs between let and const. When using const we cannot declare a variable without assigning it a value, and if you try you will receive a syntax error:


const myConst;

console.log(myConst);

myConst = 'This will cause an error!';

console.log(myConst)

// OUTPUT --> 
// "error"
// "SyntaxError: Missing initializer in const declaration"

Enter fullscreen mode Exit fullscreen mode

Aside from the example we just looked at, const behaves the same way as let when it comes to hoisting in JavaScript. The identifier name will be hoisted to the top of its scope, but its value will be uninitialised and therefore inaccessible until JavaScript reaches the line of code which assigns it a value during the execution phase.


console.log(myConst);

const myConst = 'I will be uninitialised ';

console.log(myConst);

// OUTPUT -->
// "error"
// "ReferenceError: Cannot access 'myConst' before initialization"

Enter fullscreen mode Exit fullscreen mode

If we take a look at the error message we receive, we see that it’s the same error we got when trying to access a variable declared with let before assigning it a value. It cannot be accessed as it’s not yet been initialised. There’s a term for this specific period during which let and const variables have been hoisted (so JavaScript “knows” they exist) but not initialised so are therefore inaccessible; the Temporal Dead Zone.

As a final note, although you will continue to see the use of the var keyword in JavaScript code, let and const are nowadays considered standard, with const usually being used to store objects and arrays even if you plan on mutating them later on in the program.

If you made it this far, thanks for reading!

Top comments (1)

Collapse
 
jonrandy profile image
Jon Randy 🎖️ • Edited

The important point almost everyone misses on these multiple "var, let, const" posts is that you can create variables without using any of those keywords. It's not a particularly good idea as those variables will be global, but it IS possible (not inside a module though, as those are automatically in strict mode)