JavaScript: var, let, and const
Until JavaScript ES6 arrived in 2015 there was only one way to declare variables: with the var
keyword. Now there are two more options: let
and const
. In this tutorial we will review all three approaches and discuss when each is appropriate.
The var keyword
The most important thing to know about the var
keyword is that it is function scoped. Scope refers to how a computer keeps track of all the variables in a program, specifically the environment in which each variable is accessible. Typically we talk about scope as either being global and therefore available everywhere or local and reserved for a specific block of code.
When using var
our local scope is within a function. This means that a variable with the same name can be used in two separate functions as each has their own local scope. In fact, we can use the same variable three times if we also include it in the global scope.
var greeting = "Hi from global scope!";
function sayHi() {
var greeting = "Good morning";
console.log(greeting);
}
function sayBye() {
var greeting = "Goodbye";
console.log(greeting);
}
greeting; // "Hi from global scope"
sayHi(); // "Good morning"
sayBye(); // "Goodbye"
Note that since lexical scoping means scopes can be nested, if a local variable can’t be found in the local scope, the JavaScript interpreter will move up a level to the global scope to look for it.
var color = "red";
function myColor() {
console.log(color);
}
function myName() {
console.log(name);
}
myColor(); // "red"
myName(); // ReferenceError: name is not defined
Here the function myColor
is able to access the global variable color
, however the function myName
throws an error because we have not defined a variable name
either locally within the function or globally.
The function-based nature of var
also leads to JavaScript behavior such as hoisting, where variable and function declarations are “hoisted” to the top of the scope before assignments.
The let Keyword
In contrast to var
, the let
keyword is block-scoped which means that its scope is the nearest enclosing block.
{
var x = "Ada";
let y = "Lovelace";
}
console.log(x); // "Ada"
console.log(y); // ReferenceError: y is not defined
let: function block
Within a normal function block var
and let
will behave the same if the variables are declared first:
function myExample() {
let foo = "Hello";
var bar = "World";
console.log(foo + ' ' + bar);
}
myExample(); // "Hello World"
However classic “hoisting” behavior is not exhibited by let
, a phrase dubbed the Temporal Dead Zone for some reason.
function myFunc() {
console.log(greeting1);
console.log(greeting2);
var greeting1 = "Hello";
let greeting2 = "World";
}
myFunc();
// undefined
// ReferenceError: greeting2 is not defined
It’s worth noting that the trope you might read online to describe this, “Let’s don’t hoist,” is not strictly speaking true. Both var
and let
move the variable declaration to the top of the scope but only var assigns the value undefined. You can learn more in detailed post on the Temporal Dead Zone.
let: for-loop
The most notable example of the difference between var
and let
occurs in the common for
loop.
for (var i = 0; i < 5; i++) {
console.log(i); // 0 1 2 3 4
}
for (let j = 0; j < 5; j++) {
console.log(j); // 0 1 2 3 4
}
console.log(i); // 5
console.log(j); // ReferenceError: j is not defined
In the first example, var
is function-scoped so creating a variable i
inside of a block with var
will make it available outside of the block as well. This is due to closures, which are covered in-depth here.
However in the second example using let
, our variable j
is only available within the for
loop block. That’s why the number 0-4 will be outputted but we cannot access the variable j
outside of the for
loop itself.
let: global scope
Used outside a function block – and therefore in the global scope – let
and var
perform in a similar manner.
var a = "hi"; // global scope
let b = "there"; // global scope
However within a web browser, global variables defined with let
will not be available on the global window
object.
console.log(window.a); // "hi"
console.log(window.b); // undefined
This Stack Overflow thread explains the technical reasons for this distinction.
let: redeclarations
Using var
in strict mode (you know you should always use strict mode, right?), you can redeclare the same variable in the same scope. For example, if we attempt this in the global scope as follows:
'use strict';
var x = 'hi';
var x = 'bye';
let y = 'hola';
let y = 'adios'; // SyntaxError: Identifier "y" has already been declared
const keyword
The const
keyword performs exactly like let
with one distinction: the variable binding cannot change. In other words, once you bind a variable to an object, you can’t reassign that variable.
const a = {};
a = 1; // TypeError: Assignment to constant variable
However while the binding is immutable, the variable value is definitely not constant or immutable.
const b = {};
b.myProp = 'bonjour';
console.log(b.myProp); // "bonjour"
In summary, const
has nothing to do with the value of a variable only the assignment. Thus if you use const
with an immutable value like a string ‘hello’ or a number ‘42’, it will perform in a “constant-like” manner. But used for mutable values (objects/arrays/functions), it’s possible to cause confusion since the values can be changed.
Conclusions
So which variable declaration should you use now that we have three options? Opinions vary. A large number JavaScript poobahs such as Mathias Bynens, Remy Sharp, Eric Elliott and others have advocated for preferring const
in most cases, using let
otherwise, and completely ignoring var
for ES6 code.
Others, such as Kyle Simpson have advocated a more nuanced approach: start with var
, use let
for block scoping, only use const
as a last resort for immutable values (strings/numbers).
Most likely the first approach will win out in my estimation, but at the cost of initial confusion for developers who believe const
truly means a constant value not just assignment.
Want to improve your JavaScript? I have a list of recommended JavaScript books.