Behind the Syntax Part I: let and const variables
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:
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
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.
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 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.
There is a newer ES6 syntax for creating 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!