Behind the Syntax Part I: let and const variables

July 11, 2018 · Filed under JavaScript

Back in 2015, the JavaScript language got it’s first significant update in 6 years. ES2015 – more colloquially known as ES6 – was released and started making its way into people’s workflows. With it came a type of article on JavaScript that I have come to really dislike: The “Here’s what is new in ES6!” primer.

They usually cover the very visible changes in the language like the new variable declaration keywords. I have found that these articles tend to cover the same ground in an extremely shallow way that does a disservice to the changes in the language and to the reader. They often present these new features as simple syntax changes and don’t mention the underlying changes in functionality going on.

That’s not to say the stuff I cover in these posts isn’t discussed or written about extensively, they are. I just wanted to write something specifically for people who might be coming from one of these very basic ES6 primer articles.

I’m going to break these up into a series of posts. The first one is going to be the most common example.

What they tell you: let and const

Now in JavaScript, you declare your variables with the let and const keywords. You use let for variables you know the value is going to change and const for ones that won’t.

Amazingly, this is the level of detail I’ve seen a fair amount of articles give on the topic. But there is more going on under the hood.

What they don’t: Immutability, block scoping and hoisting

The biggest practical tip I can give about these new variable declarations is that you will not need to use let very often. Outside of using counter variables or in rare cases working with primitive data types that need to change you will almost always be using const.

The reason for this is because with const, only primitives are immutable. If your const variable is an object (or array, which is a type of object), you can add and remove properties as you please. This blog post has more on the subject if you’re into the nitty gritty details.

Secondly, these new declarations are block scoped instead of function scoped. This can seem like a small change, but it’s actually really important to understand. Having a solid understanding of how scope works in JS is very important, but I will leave that to other articles. When a variable is declared with var,  it’s scope is the function it currently resides in (or global, if it isn’t inside any function.) with the new keywords, putting them inside a block gives them scope relative to that block. I’ve attached an example to illustrate.

Something else you will notice if you run that code in Codepen is that JS doesn’t have any issue with the two const declarations even though they are both string primitives. That further demonstrates that they are considered to be two different scopes by JS. If you try to redeclare const in that way in the same scope, JS throws an error.

This is important to know even if you are new to JS (and therefore don’t have any other reference point to learn from) because it’s key to understanding scope in JS, will help you debug older code and understanding the history of JavaScript can be really useful in gaining a deeper understanding of the language.

Block scoping is a useful feature that exists in a lot of other programming languages and was considered a “wish list” item by developers for a while. The creator of JS even admitted that not adding it was not a deliberate decision, but rather the result of a deadline crunch. People wrote complicated workarounds just to emulate it. In his book, JavaScript: The Good Parts (published in 2008) Douglass Crockford imagines a new, streamlined version of JS that gets rid of the “bad parts”. In this new language, he implemented block scoping.

Finally, the last major point that these new variable declarations change is hoisting. With traditional variable and function declarations, JavaScript will move the declarations to the top of the scope before executing. However, for variables it only moves the declarations up, not the initializations. So if you have the code var test = "foo"; it is only moving up var test; and giving it a value of undefined (which, despite what you’d think, is actually a value. So if you were checking if the variable existed, JS would tell you it does)

So JS will know the variable exists, but can’t detect the value until the initialization is called. It can be confusing, and if you want to learn more you can check this article out to do a deeper dive with the concept and numerous code examples.

The new variable keywords do not hoist in the same way as var. They still technically hoist (under the hood), but attempting to reference them before they are declared will cause an actual error: JS will act like they don’t exist. Here’s an example (Comment out line 2 to avoid the error stopping the full code from running)

Why the change? Well, to avoid confusion. Generally, using hoisting has been considered poor form when writing code and led to countless hours of debugging for people who did not understand this concept. This change is simply enforcing what already was good practice when writing code.

There is also a new way to write functions that avoids hoisting and is the generally accepted way to do it by most style guides. Using a function expression instead of a declaration. Here’s a simple code example that demonstrates these concepts for both variables and functions.

Of course, there is a newer syntax for declaring functions: arrow functions. Those bring along their own changes and are going to be the subject of the second post in this series.

I hope that a little bit of a deeper dive helped give some further context behind these changes. Writing these posts helps me go back over and clear up any lingering misunderstandings myself. I’m looking forward to writing part 2!

Daniel Immke

I design and build things for the web from Atlanta, Georgia. I write about topics I encounter in my day to day work here.

Learn more