tl;dr my opinion 🤔💭: use const
by default, only use let
if required.
History
JavaScript has three* ways to declare variables:
var x = 123;
let y = 456;
const z = 789;
The first, var
, comes from the original version of JavaScript. The later two arrived widely across browsers in about 2016.
Constant by Default
If you declare variables with const
, they cannot be changed ("mutated"). (If the variable points to an object, you can mutate the object.) For example:
const value = 123;
const object = {abc: 123};
object.abc++; // ok ✅
value++; // error ❌
object = null; // error ❌
This is an extremely primitive but useful method of safety while building JS. If a variable isn't meant to be mutated—it's the result of some complex operation—then const
means you can't accidentally use it incorrectly. (This is also useful in conjunction with strict mode, where you can't just invent variable names without var
, let
or const
). 🔬
Let On Demand
If you later find out that you need a variable to mutate, you can go back to its declaration and mark it as let
. This lets readers of your program know a bit more about the variable.
const counter = getTotalEvents();
// ...later, we decide to add something, but this will 💥
counter += otherEvents();
If we modify const counter
to be let counter
, readers will know that it's not the final value: it will likely mutate further on below.
Gotchas
- Function arguments are always mutable.
function foo(foobar) {
++foobar;
return foobar;
}
- You can and should use
const
inside afor-of
orfor-in
loop (which is not always obvious, since it seems like the variable is mutating every iteration). 🤔
for (const x in object) { ... }
for (const x of arrayOrIterable) { ... }
Var Considered Confusing
Declaring with var
has some quirks—the variable declaration is hoisted to the top of a function, so it's always available. Here's an example, but showing that you could just use let
instead:
Declaring with var
can be useful in some occasions, but I believe it does not follow the principle of least surprise, and you can always replace it with let
.
The Exception*
Function declarations are technically another way to declare a variable.
function foo() {
// do stuff
}
// "foo" is now something we can pass around, e.g.:
setTimeout(foo, 1000);
This isn't exactly like saying var foo = function() { ... }
, but it's similar. The main difference being that the declaration itself is hoisted, not just the variable name. Here's that in action:
foo(); // ok! ✅
bar(); // crashes: "bar" is a valid name, but contains undefined 😕
function foo() {}
var bar = function bar() {};
Function declarations are a useful concept in JS, because it lets us pass around functions before they are declared—allowing for circular 🔄 references such as passing functions as callbacks. (If you're curious, I talk about how this interacts with ES6 modules in a talk 🗣️💬 I gave at the Polymer Summit in 2017).
Thanks!
That's all for today, just a simple opinion piece!
5 👋
Top comments (17)
I’m in the “preventing re-declaration doesn’t really matter, and
let
is two characters shorter thanconst
, so…” camp.People have suggested
const
overlet
before, but I’m unconvinced. What’s so bad aboutlet
being re-declarable? I’ve been usingvar
for years and I don’t think that I’ve ever had a problem specifically with it being re-declarable.Do you mean mutable, vs re-declarable?
var
is re-declarable in that I can writevar x = 123; var x = 456;
and that's totally valid. I supposelet
is re-declarable in a lower scope (inside{}
).Ah yes, I meant re-assignable, not re-declarable.
The argument for
const
overlet
is thatconst
“protects” you from unintended reassignment, but I don’t see that as a problem that requires extra measures.let
everywhere is simple.const
andlet
together is more complex, so there needs to be a good reason for the added complexity to justify it. I don’t like making my code more complex just because something sounds like a cool feature.People who argue for
const
haven’t convinced me yet that accidental reassignment is something that I need to guard against.I hope that makes sense.
Would you use
const
for say, defining magic numbers at the top of your program?Just trying to understand if it fits into your world at all :)
const
is most likely is a very useful feature in certain situations. But what are those situations? I can only guess. I personally don’t think that I needconst
because I don’t think that accidental reassignments are an issue in my own code.So what are those situations? That’s what people arguing for
const
should focus on explaining, in my option.I think I have a good analogy. In CSS, I argue for using this code:
The
!important
part is similar toconst
. It protects thedisplay
property from being re-assigned if thehidden
attribute is present. Why is that important? Because a<div class="foo" hidden>
element could get accidentally rendered with something that is as simple as.foo { display: flex; }
. That.foo
style may not have intended to style that specifichidden
element but it did match it, and now thehidden
attribute is ignored and the element is rendered.So I think I make a good case for adding
!important
in this particular situation.I would like someone to make a good case for why
POST_TIMEOUT
needs to beconst
. What concrete problem can be prevented by that?It's about two things: semantics - code is not just meant for the computer, it's also for humans who read it. Using
const
better signifies to team members and maintainers that this variable shouldn't change. Another is safety by default. Yes, maybe your code doesn't have issues with constants. But it's about baking in safety to your code to reduce the possibility of any errors. It's like how Java class variables are package-private by default (like @flame10a said) and it's highly recommended to make them private, and variables in Rust are actually immutable (non-reassignable) by default. So, in the future, if for example you're coding while running low on sleep, or one of your coworkers decides to work on it and he hasn't worked on the project before, you are a lot less likely to make a mistake.I guess
const
makes (more) sense in large teams that work on large code bases (emphasis on I guess). I would like to see some practical evidence. If somebody was in a concrete situation whereconst
saved them (and their team) from a nasty bug, I would like to read about it, so that I can get a feel ofconst
’s usefulness.If I started using
const
, I would have to decide every time when I declare a new variable, whether or not it should be re-assignable. This mental task seems unnecessary and annoying to me. I would rather spend my brain power on the actual problems that I’m trying to solve in my code.Since I've switched to using
const
, I find that I almost never uselet
. In fact, the rarity oflet
makes it stand out in the code when it is used; it feels like a beacon to anyone reading the code that this variable is special, it's going to be updated later.But I almost never update variables anyway. There are basically two cases where I use it:
for (let i=0; i<num; i++)
looptry ... catch
situation:Neither of those are very common in my code, so I basically never use
let
.I'm glad to hear you're on the same page as me! I like the shining beacon analogy.
For me, both those examples often warrant extracting behavior to some helper function, but I understand how you're using them there, too.
That sounds like a solid approach. I’m surprised that
const
works fine in (the header of)for
-of
statements but not infor
.Yeah, the difference seems to be that in a
for ... of
loop like this:you're essentially grabbing an iterator at the start of the loop, i.e.
myThingsIter = myThings[Symbol.iterator]()
, then at the beginning of each loop runningconst thing = myThingsIter.next()
. It seems to desugar to something like this:You're always assigning a whole new value to the loop variable as opposed to modifying the loop variable as is typical in a classic
for (let i=0; i<n; i++)
loop.Really the only reason you need a
let
in that loop is because you're reassigning the value withi++
, equivalent toi = i+
. In theory you can use aconst
if you're doing something super unusual, like using an object as your loop variable and modifying a property of it:I don't know why anyone would ever want something like this, and there would undoubtedly be cleaner ways to write the code, but it works. You're never reassigning
o
, so no problem with it being aconst
.I very much agree with '
const
first'; it's in the same realm as Java class variables being private by default - no matter how safe your code may be, there's no harm in it being that bit safer!let
is of course good for any local variable that may change within its scope, but otherwise it's safer (e.g. in case of autocomplete shenanigans, or plain misunderstanding), and more visually meaningful, to declare a variable as unchangable.On the odd case of function declarations... I'm very much against using the
function foo() { ... }
syntax out of place; much likevar
, the funcionality of it can be rather backwards, and in turn can be confusing to readers of any level.You also need to remember that
let
andconst
have block scope:This will result in a log of
Actually, not mentioning this was one of the goals of my article. I think this behavior, for new programmers, is logically the most sensible!
(Having a comment pointing it out is fine though 😅)
I disagree regarding const. Spamming the keyword inflates it's meaning to me. I believe you should only use it when you actively intend a variable to be unchangeable - because otherwise when reading another person's code I would conclude you just used the keyword without thought and disregard const as a whole.
I do agree with the rest of your post in general though. Instead of using constants as a default, I'd recommend to rather put thought into your code before deciding which keyword to use.
But to quote you, this is just my opinion piece 😉
I wish it used different keywords from const vs let. Like Kotlins
val
vsvar
setup.I think there's a high number of people who don't like
const
purely because it's two letters longer, yeah. Again, this is opinion, so... ¯\_(ツ)_/¯