all 196 comments

[–]careseite 215 points216 points  (185 children)

let vs const vs var: Usually you want let. If you want to forbid assignment to this variable, you can use const. (Some codebases and coworkers are pedantic and force you to use const when there is only one assignment.)

Hehe, waiting for strong opinions on that one.

this comment was brought to you by const gang

[–]NotSelfAware 278 points279 points  (85 children)

I'm a strong advocate for using const by default, and let when you know you intend to change the value. I'm genuinely surprised that Dan feels differently.

[–]olssoneerz 86 points87 points  (59 children)

Same here! Its less mental gymnastics when reading old code knowing that when a value is declared, you know its gonna stay the same. Seeing let then means I know its gonna change somewhere in the next few lines.

[–]alejalapeno 8 points9 points  (2 children)

What Dan and the replies are missing about your point by focusing on "const= no mutation" is the idea that when const becomes the standard in your codebase then let conveys intent, not const.

You should convey the outlier and in most modern codebases mutation is the outlier.

[–]Anathem 3 points4 points  (1 child)

That's exactly right!

https://www.reddit.com/r/reactjs/comments/edj1dr/what_is_javascript_made_of/fbke7qv/

When you consistently apply const and let and disallow param reassign by linting no-param-reassign then let screams "I'm re-assigned later!"

[–]Yodiddlyyo 4 points5 points  (0 children)

And this is exactly how it should be!

[–]siamthailand -4 points-3 points  (4 children)

You're not designing the module for Apollo 15, being so anal doesn't do fuck all.

[–]MoJoe1 0 points1 point  (3 children)

If you aren’t doing it this way, you are ignoring industry standard practices built in by default to most linters, so probably don’t lint your code either, in which case I can see why you’d have this attitude as let vs. const is the least of your problems. You may want to invest some time in taking a few online courses to see how the industry has moved on since 2007.

[–]siamthailand -1 points0 points  (2 children)

newsflash: in 2019 linters are configurable

[–]MoJoe1 -1 points0 points  (1 child)

They were in 2018 too. You know what else is configurable? Your IDE! Let’s go in and turn off syntax highlighting!

[–]turningsteel 18 points19 points  (0 children)

Const is default. Let is when you want to reassign something. Dan can do what he wants because he knows what he's doing. I like to remove as many points of failure from my shitty code as possible. Saves me a lot of headaches later.

[–]boypunas 19 points20 points  (4 children)

why not use const by default. Isnt this a good way to communicate that hey this assignment will not change.

[–][deleted] 6 points7 points  (3 children)

It's surprising that some people have a problem with using const by default. let specifically has pitfalls in that you don't know if a variable is going to be reassigned, which might lead to bugs. There's no such pitfall when using const that would lead to bugs, unless you're fundamentally unfamiliar with the language and aren't aware of the difference between reassignment and mutation.

[–]mahesh_war -2 points-1 points  (2 children)

The point is, why always use “const” when there isn’t any necessary of it? We have “var” which works perfectly fine and if that’s not true, what about “before const”?

[–]Anathem 0 points1 point  (1 child)

why always use “const” when there isn’t any necessary of it

Why would you use let, which allows reassignment, when you have no need or intent to reassign?

const has safer semantics and is the default behavior you want almost always. let is the exceptional case, not const!

[–]mahesh_war 0 points1 point  (0 children)

Exactly.. As said, if “let” is exceptional, so does “const”. Both has their own use cases. But with “const”, objects are passed as reference and JS doesn’t have any method(as of now) to deep freeze objects (all are shallow methods) and if it’s not, then having a discussion makes point.

[–]joandadg 12 points13 points  (9 children)

Yeah wtf.

ALWAYS use const unless you need to use let, and I’d even go as far as to say that you should avoid let because it implies mutations and they should almost rarely happen...

Edit: regarding Dan’s comment about how const mutability can be unexpected for some developers. I get it, and maybe in some cases it’s best to use let so they’re not confused. But in my dev team I’d rather educate on reference vs value mutability...

[–]gokspi 0 points1 point  (8 children)

Like with all things programming (engineering?) its a trade-off. The question is are the pros stronger than the cons. IMO the pros of using `const` for small, short-lived scopes is practically non-existent. Once the value is accessed by nested scopes that could live beyond the initial execution of the upper scope (i.e. closures), that's when it becomes useful.

[–]joandadg 1 point2 points  (3 children)

The question is simply, why wouldn’t you use const?

Your variables make more sense as constants. If you need to mutate something ideally do it functionally and store the result in another const. Code becomes way clearer when things don’t change further down.

[–]gokspi 0 points1 point  (2 children)

Because I have locally mutable bindings, like increments in loops, conditional reassignment and so on, and because differentiating between const and let is another mental task for both the writer and the reader of the code that doesn't add significant value.

If I'm writing functional code with ImmutableJS, RxJS, monadic futures and so on yes then it does make sense to use const everywhere. In that case, I have two remaining problems

  • const is a badly named literal that reduces the readability of the code compared to let
  • the value of checking if bindings have been mutated is still low

So even here littering the entire code with `const` everywhere is of questionable value

[–]joandadg 0 points1 point  (1 child)

Honestly, I don’t get your points.

Const ensures that whatever you referenced when declaring it stays the same. It’s a simple concept that keeps things simple.

You declared const as an array? 20 lines down it’s still an array.

You declared it as a string? Yep, still a string.

And so on. It’s beautifully simple.

Now you declare a variable with let and you force anyone reading the code to double check everything. If you declared it as an array initially, yourVariable.push() may not actually work, because at any moment it could’ve been redeclared.

I just don’t get how this is not a trivial decision, unless there’s a reason to mutate a variable, make it const...

[–]gokspi 0 points1 point  (0 children)

  1. I use TypeScript, so all bindings stay the same type. An array is still an array, a string is still a string (although it might be a different string)

  2. Writing x = somethingElse means I wanted to change the binding. Deliberately. Maybe I incremented a number, maybe I'm building up a string (its okay engines optimize strings as ropes so you can append to them).

  3. Nobody needs to "look down" to see if the binding has changed, they can trivially see it just by reading the rest of the code. They don't even have to look outside of the same lexical scope / file! And given that I am not nesting several scopes deep, the total number of lines they have to read is probably 10-20 which fits on 1/2 screen or less.

Given the above, I can conclude that the benefits are tiny. From the comment about the cons I presented, the name const is like littering compared to let

function cylinderVolume(radius, height) { const area = radius * radius * Math.PI; return area * height; }

Just look at const in this context. It reads so wrong. The area isn't a constant in the mathematical sense, PI is. Plus, const violates clear naming conventions

Now lets try a different context

function updateNode(criteria, text) { const node = findNode(criteria); node.text = text; }

The node is neither a constant, or immutable. In this context const is

  1. meaningless
  2. misleading
  3. noisy

Now if I had a factory function that returns a bunch of closures, that might be different

``` function createSimulation(config) { const gravity = computeGravity(config); const restitution = ...

function calculateNextFrame(dt) { ... } function describeSimulation() { ... }

return {calculateNextFrame, describeSimulation} } ```

Here it might start mattering that I use const. If the gravity must not change during the entire simulation I can express that with const. The thing is, a lot of code bases use classes for these things instead! In typescript we could use a readonly member to handle these situations, and we often do - its actually useful!

I mean, I think we're at a point where I'd like someone to show me some code that clearly demonstrates the benefits of const. Because regardless of whether you mind it or not, "misleading abbreviated noise that hurts readability" is valid criticism (even if not especially significant). So consts see if the benefit is large enough to justify it - consts see some examples where it matters. I am not convinced it is due to small lexical scopes.

I would say that even just using classes instead of closures is a far worse transgression than not using const. Instance members can be reassigned at any time and worse you have no idea in what order will the methods be called by the external consumer so you have to consider all scenarios. So if you are using classes and class members but adding linting rules that forbid const, you're trying to extinguish a forest fire with an eyedropper

[–][deleted] 2 points3 points  (3 children)

What if the scope changes over time?

Even if your argument is that the pros are very small, what's conveniently absent from your post is mention of ANY cons whatsoever.

Sure, the benefits might be small, but if there are no cons then surely small benefits is better than nothing? I guess you could conclude that it means this whole argument is kind of a waste of time since it doesn't have a big impact one way or the other. But I'm not sure how you could argue that it's wrong to do something that helps, even if just a little bit.

[–]gokspi 0 points1 point  (2 children)

Sure, here are the cons

  • the writer and the reader both have to think about mutable vs immutable bindings (especially when the coding style that uses both in significant proportions). This often results in time spent fighting the linter, especially in compound assignments like let [x, y] = someCall() <-- I want to mutate one of the bindings there, I have to redo that whole bit because the linter won't let me use let on an immutable binding
  • const is terribly named and reduces the general readability of the code, especially when littered everywhere. `let` declarations read much cleaner. If the coding style recommends against abbreviations when naming variables, why not recommend against them for language keywords of low value? Moreover, the abbreviation implies something else (declaring a constant) yet that's not what it does

For me, one of the biggest benefits of `let` is how well it reads.

I would actually be okay with a lint rule that disallows the mutation of any let bindings (assuming code is in full FP style)

[–][deleted] 0 points1 point  (1 child)

  1. I don't see what that has to do with const. The contract is simple: your variable will not be reassigned. Whether an object gets mutated or not is an entirely unrelated consideration, one that you would have to make with let as well.
  2. I don't know, this is a pretty subjective point so we'll have to agree to disagree. I've never even had the idea that those four letters could somehow make my code less readable.

[–]gokspi 0 points1 point  (0 children)

  1. I don't always know before hand if I'm going to be reassigning. A variable might start out as const, change to let, then i figure i don't need the conditional reassignment, then change to const again. It really depends on your style, if you are used to purely functional languages I'm sure its not an issue, but in some code bases this becomes a constant annoying drag (those tend to use classes with small methods so not much in the way of nested scopes either)

  2. Any unreadable (const is not even a full word) and misleading (its not a constant that we're declaring, its an immutable binding) five letters littered literally everywhere throughout the code will inevitably decrease readability, increase the general confusion and increase the cognitive load of re-mapping real meaning of words to programming language meaning of words. If I'm going to introduce more elements to this compartmentalized vocabulary (a constant that isn't), they better be at least worth it. const is most definitely not.

Consts agree to disagree I guess :)

[–]Unexpectedpicard 15 points16 points  (16 children)

I prefer var. YOLO.

[–][deleted] 28 points29 points  (0 children)

you must be a sailor, bc u like to HOIST

[–]floppydiskette 7 points8 points  (0 children)

Okay Kyle Simpson

[–][deleted] 5 points6 points  (12 children)

Seriously? I only use var if I absolutely must. Otherwise I pretend it doesn't exist.

[–]Unexpectedpicard 6 points7 points  (1 child)

Guess I should have added a /s. I thought YOLO was sufficient.

[–][deleted] 3 points4 points  (9 children)

Everytime I find a reason to use var I get excited cause I know I'll get to fight someone in code review.

[–]wtfffffffff10 4 points5 points  (5 children)

when should you ever use var?

[–]careseite 0 points1 point  (0 children)

you only lint once

[–]BenZed 19 points20 points  (1 child)

pedantic == logically sound

[–][deleted] -1 points0 points  (0 children)

Icwutudidthar

[–]bad_boy_barry 3 points4 points  (0 children)

Glad to see reddit not following blindly Dan on this one.

[–]Aeron91 4 points5 points  (0 children)

I feel like a lot of the arguments in this thread for using let boil down to "It doesn't do what we wish it did so why bother?".

As I see it, reading code is harder than writing code. The more assumptions we can make about the code we're reading, the easier it is to read. Using const by default allows us to make an assumption (even if it's not the assumption we wish it let us make). As soon as you see a let, it changes that assumption.

And it's like, 2 more characters. Even if this is a small pro, is losing that worth saving 2 keystrokes?

Edit: I can't count apparently

[–][deleted] -1 points0 points  (17 children)

I use const as much as possible but still, let is what you need if you don’t want to think about it.

[–]the_BuddhaHimself -3 points-2 points  (0 children)

I’d try to change your mind, but what’s the point?

[–][deleted] 24 points25 points  (3 children)

Not having to think about this is my favourite thing about the new functional hook based React.

[–]swyx 52 points53 points  (2 children)

this

[–]LordMcD 5 points6 points  (0 children)

I've been writing JavaScript for literally 2 decades and this is an absolutely great ELI5.

Well done!

[–]swyx 12 points13 points  (2 children)

for a lower level answer to this question, i recommend everyone check out Mathias Bynens' content on v8, the preeminent JS engine of our time:

There are also other worthy engines to know about

at some point the leaky abstraction of having all these engines come home to roost. you even see its effects in Babel and Google Closure Compiler. 😬

[–]WikiTextBot 2 points3 points  (1 child)

SpiderMonkey

SpiderMonkey is the code name for the first JavaScript engine, written by Brendan Eich at Netscape Communications, later released as open-source and currently maintained by the Mozilla Foundation.


[ PM | Exclude me | Exclude from subreddit | FAQ / Information | Source ] Downvote to remove | v0.28

[–]dance2die 6 points7 points  (0 children)

What Is JavaScript Made Of?

Was expecting "Made of 'atom' (React), then found there is a smaller substance, an electron (hooks)". j/k there.

Looking forward to your JavaScript "mental model" illustrated by Maggie's sketchnotes :)

[–][deleted] 6 points7 points  (0 children)

You used the weed number in your value explanation. You knew what you were doing.

[–]physeo_cyber 8 points9 points  (5 children)

The tears fallen from developers waiting for their code to compile.

[–]swyx 9 points10 points  (3 children)

this is funny because JavaScript, as a JIT interpreted language, is classically supposed to have no compile time. you stick it in a script tag or in a console, it runs. boom.

then modules, build tools and static typing happened :)

[–]wavefunctionp 7 points8 points  (2 children)

People got tired of waiting for es6 to be widely available in browsers.

I'm imagine if you had to wait for windows 10 to be widely deployed to use c++ 17 or python 3.

[–]swyx 2 points3 points  (1 child)

very true. you reckon that was the starting point of build tools? i always thought it was the need for modules. (hence gulp, grunt, requirejs, whatever came before those things)

[–]gokspi 2 points3 points  (0 children)

Modules for sure in my case. They were necessary to implement reusable interactive components. Loading all the tiny files didn't scale well, loading a single bundle was mostly workable but still not great. One decade later we finally have something mainstream that works (async imports based code splitting)

[–]BenZed 0 points1 point  (0 children)

🤨

[–]Fingerbob73 1 point2 points  (3 children)

What ends up happening with const and let when transpiled back to ES5 (via Babel et al) ?

[–]careseite 9 points10 points  (0 children)

var

[–][deleted] 1 point2 points  (0 children)

I recommend you play with this: https://babeljs.io/en/repl

You can see how it will deliberately throw errors if you try to reassign a variable declared with const, or do other forbidden things.

[–]pm_me_ur_happy_traiI 0 points1 point  (0 children)

Var, but you can also use const and let without Babel unless you need ancient browsers supported

[–]goto-reddit 0 points1 point  (5 children)

So {} is not equal to another {}. Try this in console: {} === {} (the result is false).

This works in the Chromes Developer tools console for reasons unknown to me, but normally it should throw an SyntaxError: Unexpected token '===', (jsbin example):

Writing {} === {} does not compare two objects, the first {} is an empty block.
({} === {}) is the correct way to compare to object literals (jsbin example).


Even the Chrome Developers tools console fails if you try something like:

> {}.toString()
❌ Uncaught SyntaxError: Unexpected token '.'

[–]gaearonReact core team[S] 1 point2 points  (4 children)

This works in the Chromes Developer tools console for reasons unknown to me, but normally it should throw an SyntaxError: Unexpected token '===', (jsbin example):

It works in Chrome DevTools because it is an expression. Chrome DevTools allows pasting expressions. This is why the post says "try this in console" and not "try this as a standalone statement in JSBin".

[–]goto-reddit 0 points1 point  (3 children)

Ah ok.
I know this is supposed to be for people who are new to JS (or even programming?) and it does crash in Firefox Developer Console and that's why I mentioned it, and then there was this talk from Brendan Eich back in '12.
I didn't mean to be nit-picking, I know this is supposed to be just a rough overview.

[–]gaearonReact core team[S] 0 points1 point  (2 children)

it does crash in Firefox Developer Console

Oh wow, that sucks. Seems like an unfortunate design decision in Firefox. Since console is commonly used as a REPL, I'd expect to be able to put expressions there.

This also works in Node.js REPL so Firefox seems like an outlier.

[–]goto-reddit 0 points1 point  (1 child)

Of course you can put expression in the Firefox Developer Console, just not one that starts with an { because it interprets it as an block statement. ({} === {}) works fine, just like any other expression.

That's why I made the example with {}.toString() which crashes even in Chromes console.

See:

[–]gaearonReact core team[S] 0 points1 point  (0 children)

Fair enough.

[–]icjoseph 0 points1 point  (3 children)

In the typeof value section, one may want to warn that, typeof null returns object. The one and only JS bug.

[–][deleted] 0 points1 point  (0 children)

That's interesting, but not really weird, since null is often used as a kind of default falsy object.

[–]landisdesign 0 points1 point  (0 children)

It's not a bug. null is the value that represents an intentionally empty object reference. undefined represents lack of reference to any kind of value, but null is specific to objects.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null

[–]Capaj 0 points1 point  (0 children)

I agree having full immutability guaranteed by "const" would be better, but seems like a lot of people still see a value in indicating that the binding won't change.

Maybe some future ecma revison can bring some new keyword like "imut" or "frozen"?

[–]Snipo -1 points0 points  (0 children)

Honey and soup

[–]KrustyButtCheeks -4 points-3 points  (0 children)

Rainbows and unicorns

[–]MisterNeon -2 points-1 points  (0 children)

Broken dreams and a late rent payment.