How much should I be using 'let' vs 'const' in ES6?
https://softwareengineering.stackexchange.com/questions/278652
-
08-10-2020 - |
Question
I've been writing a lot of ES6 code for io.js recently. There isn't much code in the wild to learn from, so I feel like I'm defining my own conventions as I go.
My question is about when to use const
vs let
.
I've been applying this rule: If possible, use const
. Only use let
if you know its value needs to change. (You can always go back and change a const
to a let
if it later turns out you need to change its value.)
The main reason for this rule is it's easy to apply consistently. There are no grey areas.
The thing is, when I apply this rule, in practice 95% of my declarations are const
. And this looks weird to me. I'm only using let
for things like i
in a for
loop, or occasionally for things like accumulated Fibonacci totals (which doesn't come up much in real life). I was surprised by this – it turns out 95% of the 'variables' in my ES5 code to date were for values that do not vary. But seeing const
all over my code feels wrong somehow.
So my question is: is it OK to be using const
this much? Should I really be doing things like const foo = function () {...};
?
Or should I reserve const
for those kind of situations where you're hard-coding a literal at the top of a module – the kind you do in full caps, like const MARGIN_WIDTH = 410;
?
Solution
My reply here is not javascript-specific.
As a rule of thumb in any language that lets me do so in a semi-easy way I'd say always use const/final/readonly/whatever it is called in your language whenever possible. The reason is simple, it's much easier to reason about code when it is dead obvious what can change and what cannot change. And in addition to this, in many languages you can get tool support that tells you that you are doing something wrong when you accidentially assign to a variable that you've declared as const.
Going back and changing a const to a let is dead simple. And going const by default makes you think twice before doing so. And this is in many cases a good thing.
How many bugs have you seen that involved variables changing unexpectedly? I'd guess a lot. I know that the majority of bugs that I see involve unexpected state changes. You won't get rid of all of these bugs by liberally using const, but you will get rid of a lot of them!
Also, many functional languages have immutable variables where all variables are const by default. Look at Erlang for example, or F#. Coding without assignment works perfectly in these languages and is one of the many reasons why people love functional programming. There is a lot to learn from these languages about managing state in order to become a better programmer.
And it all starts with being extremely liberal with const! ;) It's just two more characters to write compared to let, so go ahead and const
all the things!
OTHER TIPS
Be careful, because const
object keys are mutable.
From here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
object keys are not protected
consider this example:
const colors = {red: "#f00"};
console.log(colors); // { "red": "#f00" }
colors.red = "#00f";
colors.green = "#0f0";
console.log(colors); // { "red": "#00f", "green": "#0f0" }
Same thing for arrays:
const numbers = [1, 2, 3];
console.log(numbers); // [ 1, 2, 3 ]
numbers.push(4);
console.log(numbers); // [ 1, 2, 3, 4 ]
I haven't decided totally myself, but I'm considering using const
for all non-array/non-objects and use let
for objects/arrays.
Don't worry about it. const
is an amazing addition to JavaScript, and I would recommend you use it all the places it makes sense. It makes for more robust code.
When it comes to objects, const
will protect your variable from reassignment, but if you need immutable objects, you will need the Object.freeze
method, see below.
const immutableOject = Object.freeze({immutableProperty: 'foo'});
const
will protect only from reassignment, and the freeze
method will protect all immediate properties. If you need all nested properties to be immutable also, then you will need to recursively freeze
them.
In my ES6 const
is not about immutability post, I explain what const
means exactly according to the spec.
Based on those objective facts, here’s my personal preference:
[…] it makes sense to use
let
andconst
as follows in your ES6 code:
- use
const
by default- only use
let
if rebinding (i.e. any form of reassignment) is needed- (
var
shouldn’t be used in ES6)
Subjective as it may be, it’s a fact that this most closely matches the spec’s intent.
People who use let
by default usually treat const
variables as constants (which they are not necessarily, by design!). To each their own, but I prefer using things for their intended purpose, and not for some made-up meaning people assign to it based on a misunderstanding.
Using const
only for constants is like using the HTML <aside>
element only for side bar content.
My personal approach, thought for helping code readability and understanding:
let
is only for short-lived variables, defined at a single line and not changed after. Generally those variables which are there only to decrease the amount of typing. For example:
for (let key in something) {
/* we could use `something[key]` for this entire block,
but it would be too much letters and not good for the
fingers or the eyes, so we use a radically temporary variable
*/
let value = something[key]
...
}
const
for all names known to be constant across the entire module. Not including locally constant values. The value
in the example above, for example, is constant in its scope and could be declared with const
, but since there are many iterations and for each one there's a value with same name, "value", that could trick the reader into thinking value
is always the same. Modules and functions are the best example of const
variables:
const PouchDB = require('pouchdb')
const instantiateDB = function () {}
const codes = {
23: 'atc',
43: 'qwx',
77: 'oxi'
}
var
for everything that may or not be variable. Names that may confuse people reading the code, even if they are constant locally, and are not suitable for let
(i.e., they are not completed in a simple direct declaration) apply for being declared with var
. For example:
var output = '\n'
lines.forEach(line => {
output += ' '
output += line.trim()
output += '\n'
})
output += '\n---'
for (let parent in parents) {
var definitions = {}
definitions.name = getName(parent)
definitions.config = {}
definitions.parent = parent
}
Further commentary and possible future updates here.
JavaScript is a bit special in that variables can be functions and such, but consider in C#, Java or another similar C style language:
const public void DoSomething()
The const
is odd, and that's because method declarations in these languages cannot change, once they're compiled into something else, that is what they do, no matter what (ignoring some horrible hacks that may exist).
Why should JavaScript be any different? So it isn't compiled, but that doesn't mean we should throw away the safety that compilers can provide. Using the const
keyword gives us more safety, which will surely lead to more robust applications.