Use const Until You Have to Use let

August 21, 2017 (7y ago)

Aloha, web surfer. So perhaps you’re a let fan who came here to see what was up. Or maybe you’re a beginner, just moving out of var town and into the wild west that is ES6 variable declaration. Whatever your experience level may be, I hope this article provides valuable insights and leaves you with a solid rule of thumb for instantiating variables in Javascript. My other hope is that, upon finishing this article, you can confidently part ways with var – it is time.

Once Upon A Time…

…in a specification authored somewhere in California…

…a keyword for variable instantiation was created and called var.

If you remember your first day learning Javascript, it probably involved var. This little keyword was our only option for declaring variables in Javascript for a long time. In fact, every web site and application that used Javascript between it’s inception in 1995 and the release of ECMAScript 2015 (ES6) used var and only var to create variables. (Someone fact check me on this statement, but I think it’s accurate.)

The var keyword is no longer relevant, but in order to truly understand our present, we must understand our past.

Some important things to know about variables created with the var keyword:

1) They are function scoped
2) Their declarations get hoisted
3) They can be declared without a value

1 “They are function scoped”

This means that var creates a variable that only exists inside of the function where it was declared.

(Unless it was not declared inside a function. In that case, it exists ~everywhere~ 😲. More on this in fact #3.)

Here’s a code snippet that illustrates the behavior of a function scoped variable:

function logA() {
  var a = 2;
  console.log(a); // inside of `logA()`, `a` exists with value 2
}

console.log(a); // ReferenceError! Outside of logA(), `a` is not defined.

I think it’s worth clarifying that when I say “inside of” logA(), I mean “between logA()’s curly brackets”.

2 “Their declarations get hoisted” 🏋

Variable hoisting basically boils down to the fact that a variable declared with var can be instantiated after it gets used.

This code…

function logB() {
  b = 2; // using b to hold the number 2
  console.log(b); // using b to print 2 in the console

  var b; // instantiating b 🤔
}

…will get transformed into something like this code…

function logB() {
  var b; // instantiation is hoisted to the top of logB()'s scope

  b = 2;
  console.log(b);
}

…at compile time (yes, Javascript gets compiled). That’s why it doesn’t matter (to the browser) where we instantiate our variables – the declarations get to skip the line every time.

There’s a dark side to the way var gets hoisted, though. Javascript’s compiler can be a bit too helpful in some situations.

If we use a variable within a function but don’t instantiate the variable in that function, the compiler will look for a var in the parent function scope. If it doesn’t find it there, it will keep looking all the way up the scope chain until it gets to the global scope. If it still doesn’t see a var declaration up there, it will just go ahead and make one for you, as demonstrated in this snippet:

function logC() {
  c = 2;
  console.log(c); // 2
}
// c is not instantiated in this program, yet it just works...

Yikes.

I guess this could be convenient sometimes when messing around in a REPL or something…

But for the most part, this is bananas. "use strict"; prevents this and should be included at the top of any ES5 script.

3 “They can be declared without a value”

You may have seen someone do something along these lines before:

// declare all my variables at the top of the script
var a, b, c;

// use them down here
a = 1;
b = 2;
c = a + b;

Put a pin in this because it becomes important later.

Times Have Changed

The three facets of var that I just covered come together to create an extremely flexible variable declaration mechanism. While flexibility itself is not a bad thing and having only one variable instantiation keyword means a little more flexibility is necessary, var is simply doing too much. Function scope isn’t a problem, but reassigning variables is so-so and should only be used for for loops and times when it’s completely unavoidable (and when it’s unavoidable, it probably means we have bigger fish to fry). The same goes for declaring variables without values. Lastly, being allowed to use a variable whether we declared it or not is silly.

So, in order to address these drawbacks of var and save us developers from the pitfalls these “helpful” features can cause, the ES6 specification introduced two new variable keywords: const and let.

Remember, the primary goal of this post is to show that it’s time to ditch var. Times have changed and var doesn’t make the cut.

Let me explain…

In 2015, Javascript’s variable declaration family grew to include let, which deviates significantly from var in that it is block scoped. This basically means that we now have two units of scope available:

1 Functions

function() {
  let i = 'aye'; // `i` is scoped to this function
  console.log(i); // "aye"
}
console.log(i); // ReferenceError

2 Blocks

{
  let i = 'oi'; // `i` is scoped to this block
  console.log(i); // "oi"
}
console.log(i); // ReferenceError

The let keyword has another helpful constraint: the variable cannot be accessed before its declaration (let does get hoisted though).

One thing let shares with var is that it can be used to declare a variable without an initial value. Like in the example above, we could have done

{
  let i;
  i = 'oi';
}

and it would’ve worked.

Bookmarks for let 👓

Const stop, won’t stop

Okay, const is my favorite variable declaration keyword in Javascript. I default to const over let and never use var. As a rule, I always use const unless I have to use let.

Remember how variables declared with var can be instantiated with empty values? This type of instantiation is allowed with let, too. It is not so with const. Oh no, const requires that you provide a value up front. Otherwise, it will turn its nose up at you and refuse to create a variable, “If you don’t know what’s going in the variable, monsieur/madame, then why do you need it at all?” – this is good.

But there’s more! const goes one step further and ensures that the variable cannot be reassigned [note: this does not mean the variable is immutable]. For example,

const a = 'ay';
a = 2; // TypeError: Assignment to constant variable.

Comparing const to var:

1 const is block scoped, var is function scoped. 2 const has to be instantiated with a value, var does not 3 const cannot be reassigned, var can 4 const cannot be accessed prior to instantiation, var can

So basically const is the strictest variable instantiation tool we have at our disposal. The constraints it provides protect us from several possible errors, making the development process smoother.

Exceptions: When to use let

While I recommend defaulting to const, some situations demand the use of let.

Here is a quote by Eric Elliot from a great article he wrote about the three keywords:

`let`, is a signal that the variable may be reassigned, such as a counter in a loop, or a value swap in an algorithm. It also signals that the variable will be used only in the block it’s defined in, which is not always the entire containing function.

In other words, for loops and mathematical algorithms are the only times we would need to reach for let. Other than that, const is the strongest, safest bet.

Use const until you have to use let

I hope this article helped you on your quest to understand let and const and how they differ from var. I also hope you can comfortably ditch var and use const until you have to use let. I’m still learning myself and there are certainly gaps in this article. Feel free to correct me when I’m wrong or bring missing information to light.