Okay this is a rant and nobody should listen to it, but listen, it's true. const is useless and totally sucks, yet it's somehow enshrined as idiomatic JavaScript.

"Const" doesn't make anything constant

const protects the variable binding from being reassigned. Because strings are immutable in JavaScript, this does prevent them (and primitives) from changing, but for objects (i.e. where all the complexity lies) it doesn't do anything to prevent field mutability.

const human = {
  name: "Jesse",
};
human.name = "Bees"; // valid
human.__proto__ = new Cyborg(); // yup, checks out

I guess it does reflect the fundamental inability for the natural world to remain constant, but I generally don't want my programming languages reflecting philosophical arguments in such detail.

const will prevent complete reassignment, like

const human = {
  name: "Jesse",
};

human = {}; // AHA! A TypeError!

But like, that's never where the confusing mutability errors come from.

The distinction in mutability actually matters in, say, Rust because the language protects against interior mutability, not just the variable binding. Not to mention it limits concurrent mutable access. JavaScript, of course, DGAF.

const human = {
  name: "Jesse",
};

await Promise.all([
  loadScript("/surname.js").then(() => human.name += " Bees"),
  loadScript("/rename.js").then(() => human.name = "Bees"),
]);
console.log(`Hi, ${human.name}!`);
// Uuuuuhhhh... Who knows what this person's name is now!

Overzealous linters hate "let"

Okay this is the rantiest bit, because I can easily change eslint settings, or at least disable it for certain spans of code. But still. You can not mix mutability when destructuring multiple values from an object, leading to unwieldy code changes like this.

let { name, honorific } = person;
if (isTuesday()) {
  honorific = "Mx.";
}
// lint error: `name` is never reassigned. Use `const` instead

const { name } = person;
let { honorific } = person;
if (isTuesday()) {
  honorific = "Mx.";
}
// eslint: Your code is now as ugly as my soul. For appeasing my cruel
// envy, I shall let you pass.

Block scoping is all anyone cares about

let and const are block scoped, i.e. they are not hoisted to the top of the script or function they're contained in. This, along with arrow functions and template literals in my opinion, is probably the most refreshing addition to the language since bind. It removes the need for IIFEs (immediately invoked function expressions) just to encapsulate temporary vars, and squashes subtle bugs that JS beginners wouldn't anticipate.

{
  const searchRegex = /\bthing=([^&]+)/i;
  let thing = location.search.match(searchRegex);
  if (thing) {
    thing = thing[1];
    console.log(`The thing the user specified is ${thing}!`);
  }
}
// neither `searchRegex` nor `thing` exist here now

Pretty great. Love keeping my scopes tidy. But that has absolutely nothing to do with the constness of the bindings, and everything to do with it not being var.

In conclusion, fuck const

So to recap, const

  • doesn't protect against interior mutability
  • doesn't protect against multiple concurrent mutable accesses (not that JS would ever do that in the first place, of course)
  • doesn't compose well with let, destructuring, and linters
  • I guess does prevent strings and numbers from getting reassigned, if anyone cares

The only good thing it has going for it is block scoping, as does let, yet it's a third variable declaration keyword for JS newbies to have to juggle. Eff that shit!

"Let" is on notice too

Look, the only reason let exists is because, while var hoisting is definitely a design flaw, fixing it would break 95% of the web. So a new keyword was needed, even though it's just a fancy var. Don't get cocky, let.