loading...

Confused by JavaScript's const? Me too!

remotesynth profile image Brian Rinaldi Originally published at remotesynthesis.com ・6 min read

The other day I had a little back and forth on Twitter around the concept of const in JavaScript. Kyle Simpson had pointed out a misunderstanding around const within an article I'd shared. My sentiment was, more or less, that I can understand where the confusion comes from since it often feels that const doesn't behave the way I'd expect it to (note, I'm not saying it is wrong, just different from my expectation).

Even in that brief conversation, a lot of terminology and concepts were thrown around. So I thought, let me dig into this a bit so I can better understand the concept of constants and the ways in which a variable declared with const actually works in JavaScript.

I should note that Kyle Simpson did write a post on this very topic, but it appears to be on a blog that is no longer live. He did share the version available via the Wayback Machine. It's worth a read as he goes into far more depth than I plan to.

What is a Constant?

If you Google "What is a constant in programming?", you'll find numerous pages that generally define a constant in the way it is defined on Wikipedia as a "value that cannot be altered by the program during normal execution."

On the surface, this seems rather simple - set a value and it cannot be changed. This can be useful for both readability and error checking. However, not all languages have constants, and those that do don't always handle them the same. For example, in some languages the types of values a constant can hold are limited.

It's once you get beyond the simple value types where things can get confusing. This is important to the conversation here (and where a lot of my own confusion around the expectation versus the reality of constants in JavaScript comes in). Rather than try to explain, I'll give an example.

Let's say I set a constant like so:

const NAME = "Brian";

It seems obvious that trying to assign any new value to NAME will result in an error - and it does. But what if I did the following:

const ME = {name:'Brian'};

If I change the value of ME.name, should I get an error? One could argue that technically, I am not changing the value of ME since it is still pointing at that same object, even though that object has been mutated. To be clear, in JavaScript, this will not give you an error.

changing the value of an object constant

It's at this point that computer science folks will get into the concepts of primitives and immutability. We'll talk a bit about these but, for the sake of not turning this into a chapter in a computer science book, I'm not going to cover them in great depth.

In brief, an immutable object is an object whose state cannot be modified after it is created. A primitive in JavaScript is "data that is not an object and has no methods." (source: MDN)

Constants in JavaScript

The const keyword was added to JavaScript in ES6 (aka ES2015). Previously, the common convention was to use a standard variable but with an all-caps name like MY_CONSTANT. This didn’t effect whether the variable could be changed - it was more a hint to tell developers that it shouldn’t be changed.

JavaScript constants declared with const can either be global scoped or block scoped. If they are within a block (i.e. between { and }) they are automatically block scoped. If they are not within a block, they are global scoped, but, unlike variables declared with var, do not become properties of the window object. Basically, the scope of a const-declared variable is always the innermost enclosing block. In case of a script, it is the global scope or, in case of a module, it is the scope of that module. (Hat tip to Axel for the clarification.)

Another interesting difference between const and var is that they are hoisted differently. When you declare a variable using const or let, the declaration is hoisted, but its value is not initialized as undefined, thus you will get a reference error if you try to access it prior to the declaration. As you can see below, the first console.log referencing a variable defined with var returns undefined but the second using const generates an error.

temporal dead zone

This is referred to as the temporal dead zone, which makes it sound much more ominous than it is.

The final important thing to note about const in JavaScript is, as discussed earlier:

The const declaration creates a read-only reference to a value. It does not mean the value it holds is immutable, just that the variable identifier cannot be reassigned. (source)

Again, this is where the confusion around const seems to emanate from. If you are using const with JavaScript primitive types (i.e. boolean, number, string, etc.), it'll behave the way you might expect (any reassignment generates an error). But, when using const with JavaScript objects (including arrays, functions, etc.), that object is still mutable, meaning properties of that object can still be changed.

For a more detailed look at scoping around let and const, there is a whole chapter in "JavaScript for Impatient Programmers" on let and const by Axel Rauschmayer.

Should You Use Const?

This is a tough question to answer, especially because let has the same benefits of block scoping and hoisting (and I cite the latter as a potential benefit since the way var is hoisted could lead to unusual errors whereby a variable is inadvertently accessed before it is declared). The people who tout the benefits of const then typically focus on code readability. By using const, you have indicated that this specific variable should not change, and it will enforce that to a certain degree.

The argument for const's readability, though, is a bit undercut by the fact that people seem to regularly misunderstand it, as we noted at the start of this article. Yes, there are some protections against reassigning this variable, but, to quote Kyle's article:

Actually, many developers assert this protection keeps you from having some unsuspecting developer accidentally change a const. Except, in reality, I think the likelihood is that a developer who needs to change a variable and gets a const-thrown error about it will probably just change the const to let and go on about their business.

So, if the protections const provides are minimal, it simply becomes a matter of style preference, particularly when choosing between let and const. If your variable will hold a primitive value that is not intended to be changed, sure, using const is a reasonable choice. However, recognize that if the value is something other than a primitive value, using const could potentially be more confusing, from a readability perspective, than helpful.

Discussion

pic
Editor guide
Collapse
ahferroin7 profile image
Austin S. Hemmelgarn

Other than the readability aspect (which is indeed big), there are a few other bnefits to using const that many people don't think about:

  • It tells the JS engine that the reference won't change. This sounds useless, but it has the potential to allow for some optimizations that would not otherwise be possible. I'm not sure whether any of the big JS implementations actually take advantage of this or not, but it's still worth considering.
  • Some IDE's, as well as some code checking tools, can be configured to complain if they see an indentifier declared with const on the left side of an assignment expression, even if the assignment would be to a property of the referenced object.

Honestly, if I know for certain that something shouldn't change, I use const, period. Yes, it can lead to confusion, but that confusion is usually less significant than properly annotating that something shouldn't change.

Collapse
georgecoldham profile image
George

You should check out Object.freeze if you want const to be immutable or Object.seal if you want const to just be a little bit mutable.

Collapse
remotesynth profile image
Brian Rinaldi Author

Thanks. Yes, I didn't bring them up because I thought it might sidetrack the discussion of const a bit but if immutability really matters for what you are doing, then definitely.

Collapse
georgecoldham profile image
George

I had always assumed they were immutable, was shocked to find they weren't.

The immutability was the key reason I started to use them.

Collapse
stereobooster profile image
stereobooster

The issue here is that there are 2 similar but different concepts: referential transparency (const) vs immutability (Object.freeze or even better recursive Object.freeze)

Collapse
jwkicklighter profile image
Jordan Kicklighter

In practice, I do what Wes Bos had recommended in his ES6 course: use const for every single variable, and only change to let if I will need to change that variable's assignment.

Collapse
remotesynth profile image
Brian Rinaldi Author

Thanks for the comment. I had received similar recommendations. A team that I worked with even had ESLint set to enforce using const for every variable where it wasn't reassigned - even objects that were mutated. This is where it caused me confusion and it was probably more common than actual primitive constants in the code. As a personal style preference, I would not choose to do that where I have the option.

Collapse
apihlaja profile image
Antti Pihlaja

When seeing let, my first thought is why that's not const? Using let communicates: watch out, we are doing some weird stuff here. Usually it signals programming paradigm change.

For example, in react app, you need maybe one or two let variables for some special cases. It steps out quite bit in the middle of declarative functional code.

Collapse
simondell profile image
Simon Dell

The difference between consts which preserve value and consts which preserve references, reflects the difference between variables that get passed by value and those which get passed by reference.

When a variable holding a primitive value gets passed to a function, the value gets copied into function's parameters ("pass by value"). The function can make changes to the value without affecting the variable's value outside the function. This holds for variables declared with var, const or let. I see a symmetry between the value's immutability, when declared with const, and the pass-by-value rules.

var bar = 'bar'
const baz = 'baz'
let qux = 'qux'

function makeFoo(fromParam) {
  fromParam = 'foo'
  return fromParam
}
makeFoo(bar) // "foo"
bar // "bar"

makeFoo(baz) // "foo"
baz // "baz"

makeFoo(qux) // "foo"
qux // "qux"

When a variable holding a complex value (variations of Object) gets passed to a function, the function cannot change the reference (pass by reference), but it can change properties of the passed object. This reflects the mutability of complex consts.

I found this observation helped me cement my knowledge about which aspects of variables const protects.

Collapse
kenhkelly profile image
Ken Kelly

I think objects are referential (like a pointer in other languages) so the const is the reference to that object. At least that is what I tell myself so I can sleep at night 🤣

Collapse
karataev profile image
Eugene Karataev

Yes, objects are passed by reference, but there are no pointers in JavaScript. We can't have direct access to memory addresses like in C language.
I wrote a mini-post describing how references in JS work with objects. By walking through a simple example, it shows that a variable can't reference another variable.

Collapse
petergabriel2 profile image
PeterGabriel2

It is not important if language provides access to memory or not. Pointer is always address to some space occupied by data regardless of programming language.
If you will have one object and this object you will put into 500 arrays, you still have only one object on the same address (on the same pointer).
So we can say that even javascript has pointers/addresses.

Thread Thread
karataev profile image
Eugene Karataev

I agree that under the hood there are pointers, memory allocation, e.t.c. I mean that JavaScript doesn't have syntax to work pointers directly and allocate/free memory like it's done in C or similar languages.

Collapse
skyboyer profile image
Yevhen Kozlov

quick search says that for Java final is also just about referencing(for immutability there is no modifier). say for C++ it's combined with sealing(but needs you to declare "change safe" methods as const too).

Collapse
petergabriel2 profile image
PeterGabriel2

I am not confused by JS consts behaviour. It acts like it should be. The same way as most of programming languages. Actually I have never worked with language that behave differently.

And differences between let and var is about scope: stackoverflow.com/questions/762011...
If you don't know what to use then use let.

Collapse
cubiclebuddha profile image
Cubicle Buddha

Should you use const?

Yes! Absolutely! It’s all about immutability.

And if you’re using TypeScript you should consider additionally using the Readonly keyword to really drive it home.

Collapse
patrixr profile image
Patrick R

Thoughts on Immutable.js ? I like the idea of using it, but it sometimes makes things more verbose than they need to be

Collapse
michaeljota profile image
Michael De Abreu

I could recommend that you use typescript instead. If you use readonly modifiers and const assertion you can have immutable code in development, and better performance in production.