Hacker News new | past | comments | ask | show | jobs | submit login

I'm surprised by the state of const/let nowadays.

The well-known good practice: use const by default; use let when it's needed. At the release of ES6, it was the way to go. But everyday I notice libraries—and some really famous— that use let everywhere in their docs, or some really influent developers from Google or Facebook who share samples of code on Twitter using let when it's not needed [1]. I don't know why. Seems like most people now think that const is for declaring constants (in the traditional meaning, like const API_URL) when it's just the normal way to declare variables that don't need to be reassigned (so basically most variables).

Dan Abramov said: "some people say const is ugly" [2]. Well, if it's a matter of appearance...

[1] https://twitter.com/addyosmani/status/789126892402204673

[2] https://twitter.com/dan_abramov/status/783708858803978240




I resisted const for like 10 minutes. You get used to it really quickly and then when you see a 'let' in your code you just know something is happening with that variable afterwards.

When I have to go back to programming in a language without const declarations it feels bad.


I know I should use const. I lazily leave it until the end then try to shoe-horn it in. Then it ripples through the code until I give up. Then I hate myself.


I've started to use const for every variable, then if I get an linting error about modifying a const, I switch it to let.

It took about two weeks to train my hands/brain to type const before let/var.


I use https://www.npmjs.com/package/eslint-plugin-const-immutable for linting const usage. It even detects object props mutation!


This is what makes Eslint so great. If you extend Airbnb's (or write your own very strict) config, it will really enforce best practices for things like this.

I think running eslint --fix will even change your "let"s to "const"s where appropriate, but don't quote me on that.


>I think running eslint --fix will even change your "let"s to "const"s where appropriate, but don't quote me on that.

Yep, this is the relevant rule - http://eslint.org/docs/rules/no-var

Though it just replaces all var with let. I expected it to intelligently use either const or let depending on how the variable is used ¯\_(ツ)_/¯


You're looking for: http://eslint.org/docs/rules/prefer-const

It does exactly what you describe :)


--fix is a godsend.


Is it just me, or is Javascript (and more generally, all front end technology) more susceptible to these trivial holy wars?

While I agree that const/let is a useful convention for communicating mutability, it isn't nearly a big enough deal to warrant the attention it receives from the community.

It's not just const/let; I rarely make a front end PR that isn't bike-shedded to death over subjective styling choices, single quotes vs double quotes, etc. It often feels like conforming to flavor-of-the-week, subjective decrees from frontend trendsetters is more important than the substantive work that your code is doing.


The fact that a const can't be changed is great but in my mind the bigger thing is what the code communicates. Using const vs let tells future devs who work on the code that the value was meant to remain unchanged. If they need to change it, that's a signal that maybe there's more to it than they currently understand.

I agree that pull requests that mass change from let to const are largely a waste of time, but usage of const vs let is a legitimate discussion within your team.


> Is it just me, or is Javascript (and more generally, all front end technology) more susceptible to these trivial holy wars?

- Tabs vs spaces.

- Vi vs Emacs

- Weak vs strong typing

- where to place {} in block statements

- where to put commas

No, programming in general is susceptible to these trivial holy wars.


Weak vs strong typing is hardly "trivial"... it's the very foundation of a language.

Not trying to derail the convo or take sides, but one of these is not like the others ;)


Hardly anyone argues weak vs. strong types, the battle is primarily between dynamic vs static typing.


Vi vs Emancs? Weak vs strong typing?? tabs vs spaces??? where did you get those??

The ONLY holy war in JS is the semi-colon one


That's what OP is saying: programming has plenty of holy wars that predate JavaScript.


You are right, I misread his comment


Clearly you have never encountered the strange "comma first" brigade.


Au contraire, for a while I was one of them. But I think we were seen as too weird to even bother with a holy war


Though I don't see why this is a holy war in the first place. Clearly, no-semicolon is the only way to go.

:^)


[loads machine gun...]


We have a little more than vi and emacs these days as competition not two sided wars.


Sublime vs Atom if you prefer.

The point still stands


Atom is clearly better until you want to open a file larger than 64 bytes.


Presumably each project/organization/etc. has its own style guidelines (or at least unwritten conventions). If you're not following them, then it's not a surprise people are calling you on it. If, on the other hand, they don't exist then it would be weirder.


const vs let is an "immutable by default" vs "mutable by default" type of difference. it's not just a style difference, it can help you write stateless code if you assume immutability.

but yeah.


Native objects in JavaScript are already immutable as in you can not change them, only create new objects. Const will not make your object immutable! Try this:

  const foo = {bar: 1}
  foo.bar = 2;
It will only avoid having the pointer re-pointed to another object. It's better to just try avoiding global variables, and use a naming convention like uppercase and/or underscore for constants and global variables.

Const is probably only meant for constants like the name suggests.

I think something like this is pretty safe from reassigning:

  {
    let foo = 1
    ...
  }
  ...
If you want more control over your properties, you can use the ES5 feature "defineproperty" where you can set writeable: false


You can call `Object.freeze()` if you want immutable properties.


    const foo = {bar: 1}
The only thing const about it is the reference `foo` cannot be reassigned:

    foo = something_else; // error
Unlike `const` in C++, `const` in javascript in not very useful in my opinion. `let` is shorter and more readable.


Even that is only true for shallow objects


All true. But what I said was,

> it can help you write stateless code if you assume immutability


> const vs let is an "immutable by default" vs "mutable by default" type of difference

Sorta. I mean you're right but at the same time if you're using const for an object or array then it doesn't really matter without `object.freeze()` because it's not immutable only the reference is and I don't think most JavaScript developers understand that (at least not most of the ones I've run into).


Note that Object.freeze only works for shallow objects


> Is it just me, or is Javascript (and more generally, all front end technology) more susceptible to these trivial holy wars?

What constitutes holy wars are often the most accessible[1]

[1] https://en.wikipedia.org/wiki/Law_of_triviality


The arguments are so bitter because the stakes are so low.


It should have been "let"/"let mut", not "const"/"let" (or some other scheme that makes immutable bindings terser). I recall people warning of this outcome at the time of standardization.


I don't think it's a terminology problem. In fact, "let" and "const" are probably the right terms. Both are descriptive and exist in other programming languages.


`final` is a better term for real constants


I wish they had come up with something else than "const". I can't not see it as anything other than "the traditional meaning like const API_URL". Those writing the spec should have forseen this and used something else. I suspect that this is something like arrow functions. The concept of arrow functions is great, but using something that looks like "equal to or greater than" was poor judgement. I know that this was taken from other programming languages and I can't help but suspect that using const to mean something other than it's tradition meaning is something borrowed from some other language.


How do you feel about `const` for variables that are not reassigned but are still mutated?

    const array = [1, 2, 3];
    array.push(100); // unexpected?
I prefer to use let for bindings to objects that get mutated, even if the variable is not re-bound.


Yeah, I wrestled with that briefly.

For anyone not in the know, declaring `const` on an object only makes it where you can't re-assign the object, but you can still mutate its 'members'.

Personally, I use `let` just to symbolize that it isn't a constant and does change even though I could declare it a `const`.

I think it makes the code more readable.


As far as I know, the "correct" approach is to act as if every object is deeply frozen (perhaps actively testing using Object.freeze... IMO https://github.com/ecomfe/babel-plugin-freeze-const has the right idea but is nothing more than an idea), and transform things into new objects functionally, using .map and friends.

The problem is that, while a lot of things can be expressed elegantly this way, sometimes the most maintainable thing, or the most efficient thing, is to have some side effects in a deep loop in a certain algorithm, and it's very difficult to do this with purely functional JS.

The real solution is to allow your const bindings to be mutated when you need to, and comment explicitly to make things perfectly clear!


It doesn't help that many variables are objects, and const means that you cannot reassign the variable, not that you cannot modify it.

e.g.

    const x = {};
    x.foo = 'it works!'


This is the same as a const ptr in C: the pointer can't change but the value it points to can. While it may seem broken it has its uses. Doing deep watches to disallow object mutation would be impossibly expensive (I think?).


Not sure if this is valid C syntax, but in C++ you can do something like:

  type const * const
Which is a const pointer to a const type, making both the pointer and the data immutable.


Maybe in ES9 we will have

    immut x = {};
    x.foo = 'it doesn't work! :)'
At the moment, we can use these libs to achieve it

- https://github.com/rtfeldman/seamless-immutable

- https://github.com/facebook/immutable-js


We don't need any libraries to solve this issue (please don't bring in libraries to do weird stuff like this; that's a dependency that you'll be stuck with forever over your entire codebase for essentially zero reason IMO).

Just use `const` + `Object.freeze()`; it'll get you 99.9995% of exactly what you want.


I'll give you two reasons: performance and ease of use. When you need a copy of a large object with a small change, performing the copy with native JS is going to be slower than doing it with a specialized data structure like a hash mapped trie[0] (which is what Immutable.js uses). Also, if you're trying to keep your data truly immutable, that copy operation is going to be a pain to write with the built-in tools, whereas it's super easy to return a copy of an object with a change to a single, deeply nested property with Immutable.js. I agree that it's premature to reach for a library before you need it, but let's not pretend there aren't rather large drawbacks to using Object.freeze and Object.assign.

[0]: https://en.wikipedia.org/wiki/Hash_array_mapped_trie


> When you need a copy of a large object with a small change, performing the copy with native JS is going to be slower than doing it with a specialized data structure like a hash mapped trie[0] (which is what Immutable.js uses)

Fair enough though I'm not convinced you should be hitting this type of use case in your code (kinda inefficient and sounds awkward to make a small change to a large object and yet need both objects to continue to be in memory, separated). At least not typically / frequently.

> if you're trying to keep your data truly immutable, that copy operation is going to be a pain to write with the built-in tools, whereas it's super easy to return a copy of an object with a change to a single, deeply nested property with Immutable.js.

While it is a little bit of a pain almost every framework and probably half of the libraries in existence on npm have a copy function. I'd like to think it's a rare use case but when it's needed you likely don't have to install a new library to handle a copy operation.

> I agree that it's premature to reach for a library before you need it, but let's not pretend there aren't rather large drawbacks to using Object.freeze and Object.assign.

I'm not sure anyone was pretending anything of the sort here and I don't understand the assumption of such. There are plenty of drawbacks but I'm also not convinced it doesn't fit the 95% use case.


Sorry, that might have come off a bit harsh. I was just trying to make the point that Object.freeze doesn't give you nearly 99.9995%, nor even 95%, of what immutable data structures give you. I don't think the apps I'm working on are particularly complex, but if you're using a Flux architecture with something like React/Redux, this is a very common pattern, and it doesn't take long to get to a point where the gains in performance and usability far outweigh the cost of having a dependency.

> While it is a little bit of a pain almost every framework and probably half of the libraries in existence on npm have a copy function.

The problem isn't just the usability of the copy operation (and even if it were, you still have the performance issue); there is also the problem of JavaScript not enforcing immutability of nested objects with Object.freeze. So if you want to enforce that, you're going to have to call Object.freeze on every child object, which becomes a nightmare to write and maintain.


> I was just trying to make the point that Object.freeze doesn't give you nearly 99.9995%, nor even 95%, of what immutable data structures give you.

Sure. That's why I made sure to say it'll give you 99% of what you want. Not 99% of immutable data structure capabilities :).

Though I'm sure folks may disagree with what I'm suggesting they "want" but I do not believe it's a good pattern to follow where a complex object needs to be immutable in JavaScript.

> JavaScript not enforcing immutability of nested objects with Object.freeze. So if you want to enforce that, you're going to have to call Object.freeze on every child object, which becomes a nightmare to write and maintain.

Which is why you should never ever do that. It's a bad pattern in a dynamic language to try and make complex structures completely immutable. It's best to simply find alternative ways of accessing them if you do not want to expose it to modification.


  const x = Object.freeze({
    y: {
      foo: 'bar'
    }
  });
  x.y.foo = 'baz'
  console.log(x.y.foo) //baz
immutablejs might be overkill, but not having, and using, a recursive freeze is going to bite a lot of people if the advice is just 'const + Object.freeze'.


> immutablejs might be overkill, but not having, and using, a recursive freeze is going to bite a lot of people if the advice is just 'const + Object.freeze'.

No one should be trying to use a recursive freeze (if they are I would argue their data structure is poorly suited to be immutable).

I'm not saying `const` + `Object.freeze()` gets you Immutablejs I'm saying it gets you, likely, what you want / need.


Our team was just hit with this:

  const x = Object.freeze([
    {id: 1, value: 'foo'},
    {id: 2, value: 'bar'},
    {id: 3, value: 'baz'},
  ])

  ...

  //Bug in the code
  x[0].value = 'test'
First comment on the bug report was:

'This shouldn't be possible as the array is const and frozen'.

their data structure is poorly suited to be immutable

An array of object seems completely reasonable. Even an array of objects, where those objects themselves have keys which are objects/arrays doesn't seem unreasonable.

We'll just have to agree to disagree.


> First comment on the bug report was: > 'This shouldn't be possible as the array is const and frozen'.

Yeah const and Object.freeze() are not exactly the most intuitive depending on your level of JavaScript internals knowledge (and even then I don't think const is very intuitive but I digress).

> We'll just have to agree to disagree.

Fair enough! I would just caution trying to make complex objects immutable; it can be handy if you're developing a library and don't trust the dev on the other side (to a degree, copying may be preferable depending on the context) but it can complicate things and lead to some performance and development pattern issues.


`const` means that the variable binding itself is immutable. It only affects the variable binding, not the value it points to.

If it affected the value it pointed to, what would happen in this type of situation?

    let x = {};
    const y = x;
    x.a = 5;


This is trivially testable in most browsers inspection tools.

The answer is it works fine. x.a === y.a === 5

This is because you are simply declaring the binding of y to the object bound to x constant. This does not impact your ability to rebind x or to alter the contents of the object, it simply prevents you from rebinding y.

    let x = {a:5}
    x = {}
    console.log(x.a) // undefined
---

    const y = {a:5}
    y = {} // Uncaught TypeError: Assignment to a constant variable.
---

    let x = {a:2}
    const y = x
    x.b = 12    
    x = {}
    x.b = 13
    console.log(y) // {a: 2, b:12}
    console.log(x) // {b: 13}


The question was rhetorical to try to demonstrate that it might result in surprising behavior if const just manipulated its values to make them become immutable. If adding the `const y = x;` line made the object referenced in `x` be immutable, then the 3rd line would fail, which I don't is a consequence desired even by people who thought const made things immutable.

I guess one alternative idea for how const would work could be an implementation where `x.a = 5` worked but `y.a = 5` failed. But then what happens if you pass `y` into a function which then tries to assign the `a` property on it? Is the const-ness part of the value passed into the function, or is it a property on variable bindings, and you could only pass `y` to functions that accepted a const variable? That kind of function type checking isn't something usual to javascript currently. And then is the const-ness deep? Is `y.foo.a = 5;` blocked? Mutable DOM elements are a big part of javascript. If the object happened to contain a reference to an element that needed to be manipulated, then you won't be able to do something like `y.foo.element.textContent = "new foo content";`. Going down this road it's now getting to be a pretty big feature that doesn't cleanly fit with the rest of the language or common tasks.

Maybe the naming is a little unfortunate: Javascript's `const` has more in common with Java's `final` than C's `const`.


Oh I didn't realize that. I assumed that const means immutable.

Now I am going back to change all the object declarations to const.


I heard interesting argument against const, it went like this:

Const very rarely saves you from bugs and the bugs that saves you from are very easy to find and fix. On the other hand the time wasted by thinking where whether to write const or let and the time wasted by switching consts for lets (and other way around) outweighs the time saved by potentially preventing these easy to find bugs. To sum it up, if consts does not really provide any extra value, why not make your life easier and just use let everywhere.

It was from very senior c++ programmer, so I am not sure how well that translates to JavaScript.


That makes a lot of sense for c++ constness, which has virtually nothing to do with JavaScript's const. C++ constness is significantly more far-reaching and therefore more work to get right. I agree with his position.

JavaScript const is just a matter of typing 2 more characters and in exchange you make your code more readable (because you communicate "this stuff will never change from here on", which makes understanding the stuff that does mutate easier). There are no far-reaching consequences. Just type "const" everywhere.


> C++ constness is significantly more far-reaching and therefore more work to get right.

I'm not familiar with C++, can you explain more?


A const variable means that variable won't change. So `const int i = 6` means i will never change. But often you'll use pointers or references, these are C++'s way for a variable to point to data elsewhere. You can also make the pointers or references themselves const. Finally, you can make functions const too, which lets you turn C++ into half a haskell.

First pointers and references. For simplicitly I'm going to ignore references and focus on pointers - the difference is not very interesting in this context. In most languages you probably know, referring to objects is done implicitly: variables that "are" objects are actually references to objects located elsewhere, and variables that are primitive types (numbers, booleans, strings) are just right there. Because of this, in JavaScript you can do this:

    var a = {};
    var b = a;
    b.moo = 6;
    a.moo === 6; // true
In here, a and b point to some object that "is" neither a or b - the object itself just floats around in memory and it'll exist until neither a, b, nor anyone else points to it anymore and the GC decides it has to go.

In C++, you'll need pointers or references for this, eg.

    somestruct a;
    somestruct* b = &a; //b now points to a
    a.moo = 6;
    b->moo == 6; // true
(-> is just C++ shorthand for "follow the pointer and then do a property lookup).

Ok now const.

    somestruct const a;
    a.moo = 6; // error! can't modify a const.
Ok well how about

    somestruct a;
    somestruct* const b = &a;
    b->moo = 6;
    a.moo === 6; // true
This means the pointer is const. That works because we never change where b is pointing to. So how about:

    somestruct a;
    somestruct const* const b = &a;
    b->moo = 6;
    a.moo === 6; // true

Oh damn. Crying baby. Const functions will have to wait for some other day.


If you didn't want a to be mutated, you'd make it const in the beginning :)

Rust makes it much harder to make these kinds of erros by making variable ownership, reference lifetime, and const-by-default core to the language design. https://doc.rust-lang.org/book/ownership.html ... You do end up spending a lot of time "fighting the compiler," even compared to template-heavy C++ codebases, but the systems you create are free from an entire class of errors.


I'm not sure if you meant to imply otherwise, but your last example won't (and shouldn't) compile with clang/gcc.

clang++: error: cannot assign to variable 'b' with const-qualified type 'const somestruct *const'

g++: error: assignment of member ‘somestruct::moo’ in read-only object


It also doesn't help that let is 3 letters versus const is 5; programmers if anything will default to the faster to type option.


This. I personally agree with everyone saying const > let, but I just don't find myself caring enough about it to add the inconvenience of typing 5 characters instead of 3 every time I need to create a variable.


You'd think after all these decades I'd no longer be surprised that programmers try so hard to minimize their typing, since it has no demonstrable positive impact on code quality. I guess premature optimization is in the blood of some people.


It's not about working hard to minimize typing, it's more about not wanting to switch from a state of little typing to a state of more typing. Also, it has nothing to do with optimization; I don't think there exists a single person who uses "let" over "const" because of performance reasons. I also don't think anyone uses "let" over "const" because they think it improves code quality.


The right path have to add more value then the path of least residence.


True, which is why we drop any var/let/const for prototyping


It also doesn't help that in Swift, 'let' is equivalent to JavaScript's 'const', so if you write code using both languages, it's easy to forget that and just use 'let' everywhere.


The only difference between const and let is it flags an error on reassignment (in strict mode), so that is its primary purpose. It can also be used to conspicuously note intent not to reassign but it is not helpful to VMs or programmers to explicate whether or not each and every variable is reassignable. Maybe just get on board with those influential developers and use const to lock things down (in strict mode) or to make a point - not because let is 'not needed'. I would rather const was used somewhat sparingly, giving it narrative impact.


it's sad the longest of [var, let, const] is the most useful one. They should have made let behave like const




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: