Explaining the difference between var, const and let in Javascript

Published

As is often the case with programming languages, there are multiple ways to define and assign values to variables. In traditional Javascript fashion, things have evolved over the year and the wisdom found on the internet might not be up to date.

Table of Contents

let vs var

var is the old way of defining variables. It used to be the only one but came with significant issues like not respecting scope and letting you redeclare already existing variables. Let’s go through a few examples:

In this first example, we define a variable myVariable using var in the root scope. We then define myVariable again in a smaller scope. We expect the outer myVariable to be untouched and that the inner myVariable will just shadow the name of the outer one. That’s not happening and we end up actually replacing the initial variable.

var myVariable = 0;
{
	// We're creating a smaller scope and redefining myVariable in it
	var myVariable = 1;
}

console.log(myVariable); // <- prints 1 instead of 0

In the following example, we only define a variable using var in a smaller scope. We expect it to be dropped as we leave this scope but that’s not happening. Instead the variable is accessible and initialized globally.

{
	// We're creating a smaller scope and defining myVariable in it
	var myVariable = 1;
}

console.log(myVariable); // <- prints 1

Let’s compare this behaviour with let:

let myVariable = 0;
{
	// We're creating a smaller scope and redefining myVariable in it
	let myVariable = 1;
	console.log(myVariable); // <- will print 1
}

console.log(myVariable); // <- will print 0
{
	// We're creating a smaller scope and defining myVariable in it
	let myVariable = 1;
	console.log(myVariable); // <- will print 1
}

console.log(myVariable); // <- ERROR: myVariable is not defined

Right away we realize that by using let, our variables will always respect their scope. The shadowed or plain inner myVariable ends up being dropped once we leave its scope without having had any impact on the outer variable and scope. As a bonus, let will also prevent you from redeclaring a variable, which can be very helpful when migrating an older codebase.

As a rule of thumb, you should always prefer using let to var. By doing so you protect yourself from a litany of difficult to track bugs at no cost.

const vs let

Just like let, const is a more recent addition to Javascript and brings the same benefits. Just like the name implies, const is dedicated to variables that are constants. Which means defined once and never changed afterwards, you can think of them as read-only variables.

Let’s look at const in action:

const myVariable = 0;
{
	myVariable = 1; // <- Error: assigning to a constant variable
	console.log(myVariable);
}

console.log(myVariable);

An important caveat here, while this works fine for simpler types like strings and numbers, const does not prevent modifications to complex types like arrays or objects, it will only prevent re-assigning to the variable. Let’s look into this:

const myObject = { hello: 'world' };
myObject = { hello: 'reader' }; // <- will fail

As explained, const will prevent you from reassigning to the myObject variable. But what if you try to change a field on that object?

const myObject = { hello: 'world' };
myObject['hello'] = 'reader';
console.log(myObject); // <- prints { hello: 'reader' }

const myArray = ['javascript'];
myArray.push('nexus');
console.log(myArray); // <- prints ["javascript", "nexus"]

Well, turns out you can and your constant is not as much of a constant as you thought. I won’t go deeper into the subject but this is due to the fact that Javascript uses references to deal with these more complex objects and const doesn’t handle this so well.

As an aside, if you’re looking to have an actual constant object, I’ll recommend to use Object.freeze in conjunction with const, let’s look it up:

Bonus: freezing an object, the true constant in Javascript

Object.freeze is natively available in Javascript, no need for an additional library. Here’s an example:

const myObject = Object.freeze({ hello: 'world' });
myObject['hello'] = 'reader';

console.log(myObject); // <- prints { hello: 'world' }

const myArray = Object.freeze(['javascript']);
myArray.push('nexus'); // <- will actually fail
console.log(myArray);

As you can see, the actual behaviour will change slightly depending on the underlying type you’re trying to freeze and how you’re accessing it but what matters is that the object will be “frozen” and become truly constant with no way to add additional properties or modify existing ones.

Conclusion

When should you use var in Javascript?

  • Never, plain and simple

When should you use let in Javascript?

  • Always where you would have considered using var
  • When you do not expect your variable to be constant

When should you use const in Javascript?

  • Whenever you can
  • When you want to communicate that a variable should not change during its lifetime
  • Consider the additional use of Object.freeze when dealing with more complex types
#language