With the release of ES6 (or ES2015, whichever you prefer) in 2015, we were given two new ways to define variables: let and const. To understand what value these provide, we'll take a look at the original way to define variables with var and its problems, then we can see how let and const overcome these.
varSince the beginning of JavaScript, var has been the standard way to declare variables. Let's look at some of the strange behaviors you might have encountered when using var.
Variable hoisting is an interesting behavior in JavaScript which you've probably run into without even realizing. Take a look at this:
console.log(myName); // this should produce an error, right?
var myName = "Nick"; // …since the variable is declared afterYou'd probably expect that console.log(myName) to throw an error (since it's defined after), but what you actually get is undefined (the same as if you'd defined the variable without a value.) Here's how the code is actually executed, using variable hoisting:
var myName; // the declaration is moved to the top
console.log(myName);
myName = "Nick"; // …but the assignment stays where it wasA "block" in JavaScript is any code inside of curly braces ({}). Let's take a look at how var behaves within blocks:
var height = 1080,
isPortrait = true;
if (isPortrait) {
var height = 1920;
console.log(height); // this prints 1920
}
console.log(height); // this also prints 1920You might have expected the last console.log(height) to produce 1080, since we're redefining height inside of the block and the console.log is outside of the if block. At least the variable declaration inside of the if block should have thrown an error since that variable was already defined. However, because var isn't block-scoped, it will refer to the variable defined outside of the block even when referenced inside the block. Additionally, JavaScript will ignore if you try to redeclare the variable.
letNow, let's explore how let can help us overcome some of the issues associated with using var.
Unlike with var, the hoisting behavior doesn't happen with let. This leads to more logical and predictable code:
console.log(myName); // now, this throws an error
let myName = "Nick"; // …since the variable is declared afterThis forces us to declare our variables first, before attempting to use them:
let myName = "Nick"; // declare our variable first
console.log(myName); // now, we can access itRemember back when we looked at hoisting with var? The variable declarations were always moved to the top, while the value assignments stayed where they are. Since we don't have that now with let, we're left with an area of code before the variable declaration known as a "Temporal Dead Zone":
/*
Any area of code before the declaration of the variable
is a "temporal dead zone" in reference to that variable.
In other words, that variable doesn't exist here.
*/
let myName = "Nick"; // oh good, we're out of the temporal dead zoneIn reality, it's just a big name for a super-simple concept: you can't access a variable until you define it.
As you saw with var, it completely ignores when we try to redeclare a variable inside of a block, but watch what happens with let:
let height = 1080,
isPortrait = true;
if (isPortrait) {
let height = 1920; // variable is scoped to this block
console.log(height); // this prints 1920
}
console.log(height); // we're out of the block, this prints 1080This strict behavior of let leads to more predictable, more readable, and easier to debug code, by avoiding the forgiving behavior of defining variables with var.
constconst has all the same characteristics of let, but with one main difference: you can't reassign its value. However, as we'll see below, its value can still change, under certain circumstances.
This name seems to suggest that when you define a variable with const you'll have an unchangable, constant value (like in other languages), but as you work with const, you'll find that isn't always the case.
What const does is prevent you from reassigning the value (or reference) of the variable. If you use a primitive, like a string or a number, any changes to this value actually causes JavaScript to create a new value and reassign the reference.
const systemName = "Nintendo Entertainment System",
releaseDate = 1985;
// let's try to "reassign" these values
systemName = "Sega Genesis" // this will throw an error
releaseDate = 1989 // …and this will tooHowever, when you're working with objects (remember arrays and functions are objects too), which are reference-based, you'll find that as long as you don't reassign the variable, you can still make changes to the properties.
const system = {
name: "Nintendo Entertainment System",
releaseDate: 1985,
};
// let's try to "reassign" the properties of our variable
system.name = "Sega Genesis" // totally acceptable
system.releaseDate = 1989 // …and so is this
/*
Our "system" object now looks like this:
{
name: "Sega Genesis",
releaseDate = 1989,
}
*/As you can see, we can change the properties of an object, since we're not reassigning (or repointing) the variable; it's still referring to the same place in memory. Just to dig in a little more, let's try to reassign the object:
const system = {
name: "Nintendo Entertainment System",
releaseDate: 1985,
};
// What happens if we try to reassign the object as a whole?
// this will totally throw an error…
system = {
name: "Sega Genesis",
releaseDate: 1989,
}This behavior is something you'll want to be aware as you're working with const. You might expect an object's values to be the same as when you declared them, and be surprised when they're not.
My rule of thumb: always use const, unless you absolutely need to change the value, then use let, but never use var. I've read about some use cases where using var would be beneficial, but I believe the downsides to using var far outweigh any potential benefit. Using let and const exclusively will give you more stable code and help prevent a lot of unexpected side-effects.