When to Use var or let or const

Abstract image used to represent When to Use var or let or const
Image by Maximalfocus.

In Brief

Use const for a binding that will not be reassigned, and let when reassignment is part of the design. Reserve var mainly for deliberate work with legacy code or functionscoped behaviour. A const binding prevents reassignment of the binding itself; it does not make the properties of an object or the items in an array immutable.

With the introduction of ES6 back in 2015, a lot of new major features were introduced to the JavaScript language. Two of those features are the let keyword, and the const keyword. They give us two new ways to declare variables in JavaScript but why would we need them? Isn't var good enough already?

As you might expect, these weren't changes without cause; var, let, and const all have their own specific use cases where they make the most sense. Let's take a look at a few of the differences between these keywords below, starting with the one we're probably all most familiar with: var.


The var Keyword

var declares a variable in its containing function or at the top level. An ordinary block, such as an if block, does not give var a separate scope. A toplevel declaration belongs to the module in an ES module; in a classic browser script, it can create a global binding and a property on the global object.

{
  var foo = 'bar';
  console.log(foo);
  //=> 'bar'
}

console.log(foo);
//=> 'bar'

This is where scope begins to matter. The block above does not hide foo; both calls can read the same variable.

A var declared inside a function is local to that function. The risk in this example is reusing a name within the same scope, not that every var becomes global.

For an example, let's take an oldschool for loop:

var i = 500;
for (var i = 0; i < 1000; i++) {}

console.log(i);
//=> 1000

When run, the console will output 1000 here, rather than the 500 you might (reasonably) expect. This might not seem immediately like a huge issue especially in an abstract fourline example but in a full web app it might have some undesired consequences; redeclaring variables and ignoring block scoping can be an issue, especially when using singleletter variables for simple things like i to represent iteration indexes

Paying more attention to the scopes that your variables are accessible in, and being strict with them, can help you keep things neat when working on large, complex sites or web apps and cut down on that spaghetti code. This is where the real magic of let and const become apparent.


The let Keyword

let lets us reassign a variable but not redeclare the same name in the same lexical scope. Unlike var, it also gives an ordinary block its own binding:

let i = 500;
for (let i = 0; i < 1000; i++) {}

console.log(i);
//=> 500

Here, the loop has its own i binding. Incrementing it does not change the outer i, so the final call logs 500. That distinction comes from block scope; the outer variable does not need to be global.

However, there is another key benefit to bear in mind here, too; let prevents redeclaration.

var foo = 'bar';
var foo = 'foo';
console.log(foo);
//=> 'foo'

The var example logs "foo": redeclaring that name in the same scope is allowed. By contrast, put let bar = 1; let bar = 2; in a separate script and it fails during parsing with a SyntaxError. None of that script runs, including any earlier logging statements. A browser may report it as follows:

Uncaught SyntaxError: Identifier 'bar' has already been declared

The duplicate declaration is rejected before execution. This checks whether the same binding name is declared twice in one scope; it does not enforce a variable's value type.

It should be said here: the other key difference between let and const (which I will talk about below) is that let can be reassigned a new value very simply:

let lorem = 'ipsum';
lorem = 'dolor';

console.log(lorem);
//=> 'dolor'

Here, the console will log out dolor because although lorem was originally assigned to ipsum, it was subsequently reassigned to dolor.

So, let's finish up by taking a look at const.


The const Keyword

const declares a binding that cannot be reassigned. Like let, it is blockscoped and cannot be redeclared in the same lexical scope. Redeclaration produces a SyntaxError; assigning a new value to an existing const binding produces a TypeError when that assignment runs:

const foo = 'bar';
foo = 'foo';
console.log(foo);
//=> will not run

This example reaches the assignment to foo, throws a TypeError and never reaches the following console.log(). That is a runtime failure, unlike the duplicate lexical declaration discussed above:

Uncaught TypeError: Assignment to constant variable

That restriction protects the binding, rather than enforcing a fixed JavaScript value type or making an object immutable.

It is however worth bearing in mind that although a const variable cannot be reassigned, some object properties and array data stored within a const can be. Ideally, though you should intentionally choose to use const in situations where you do not want or need the variable to change.

There is one more useful distinction, often described as hoisting. Before its declaration executes, a var binding is already initialised to undefined. A let or const binding exists in its scope but stays uninitialised until its declaration is evaluated. Reading it before then throws a ReferenceError; that period is called the temporal dead zone.


The Wrap‑up

var, let, and const all accomplish essentially the same job; creating and declaring variables. However, let and const accomplish this in a much more structured and responsible way, allowing for clearer code and easier management of that code, by limiting scopes and preventing redeclaration. There might be times when you want to affect the global scope by using var, but by and large, it makes much more sense to be mindful and intentional with your usage of scopes when working with variables, even if just for the sake of the developer who comes to the code after you...!


Have a complex web platform issue?

Tell me what is blocked, what has changed, and what needs to be true after the fix. I'll come back with a practical next step.