const
vs fucking let
I didn't want to be the guy write this post, but I am because Chris told me to so blame that fuckin guy. I'm a victim here.
But you're all using const
in JavaScript too much and it
does bug me just ever so little.
const
doesn't do shit and we all know it
For starters, let's agree that const
is fucking useless and
we're inventing meaning...
const THIS_SHOULDNT_CHANGE = { right: "?" };
THIS_SHOULDNT_CHANGE.right = "wrong";
THIS_SHOULDNT_CHANGE.fucking = "deal with it";
const
does not imply any kind of immutability of the value
itself, it only implies immutability of the binding.
Which... Cool.
Let's also eliminate the idea that we could somehow statically enforce that const values are immutable with a linter.
Like, sure, you could for the simple case:
const FOO = { prop: true };
FOO.prop = false; // LINT ERR >XD
But then like... side effects exist and shit will hurt you
export function mutatesProp(obj) {
obj.prop = false;
}
import { mutatesProp } from './mutatesProp';
const FOO = { prop: true };
mutatesProp(FOO); // no lint err :(
And yeah, type systems like Flow and HiHaveYouTriedTypeScriptYouShouldReallyTryTypeScriptIWriteTypeScriptAndImFuckingObnoxiousImGonnaStickMyHeadInAMicrowaveAndScreamTypeScriptâ„¢ could do something with this, but like... they don't... sooooo
You might be thinking, "Oh but it can still be used to optimize code or whatever"
You would be right in thinking that compiler optimizations would make it interesting enough to use. But...
You would be wrong in thinking that const
is at all useful
to optimizing compilers.
It's already really really easy to identify a binding and figure out if
it's never reassigned. All of the relevant optimizations are already
possible without const
ever being a thing.
Also, V8 and friends have been doing these optimizations without
const
for a really long time. Benedikt or Franziska or
whoever can correct me if I'm wrong, but Babel definitely can already do
this shit.
First off, like... we don't have to do anything. There is no
reason you would ever have to use const
, and
whereever we are using it, we could just as easily use let
and our code wouldn't be impacted in any way.
Honestly, if you just want to stop here and never use const
again and never question it further, you will save literally whole...
minutes of your life.
But like... we have const
so why don't we use it for
something ...right? We could like use it to communicate.
Sure.
So, lots of people (okay maybe not you, you special little shit) are
doing this... thing where they change every let
variable which is never re-assigned to const
.
let answer = 42;
neverChangeTheAnswer(answer);
justKeepDoingOtherStuffWithTheValue(answer);
They would see the above code and say "that let
should be a
const
so that the reader knows at the declaration of
answer
that it never changes."
And many of them will include linter tools for enforcing that rule on every variable declaration, even sometimes applying the "fix" automatically.
const answer = 42;
neverChangeTheAnswer(answer);
justKeepDoingOtherStuffWithTheValue(answer);
"That's better."
But... is it better?
Did you really mean to communicate that
we should never change the answer
or do we just
happen to never change it?
What if we came along later and decided we are okay changing the answer?
But now it's a const
and we aren't really sure if we're
going to break someone's expectation for answer
if we do
change it.
There are lots of bindings that we happen to never change that we're totally fine if they do change. In fact, that's probably true for most bindings. It's not something your code will generally care about.
But by automatically applying this rule to every binding no matter what the author's actual intent was, we actually throw out our opportunity to communicate something.
const
for?It's probably still a good idea to communicate that you really don't intend for something to be changed.
But in order to communicate that, we need to only use it when appropriate.
const I_ACTUALLY_GIVE_A_SHIT_IF_THIS_CHANGES = 42;
If you want some good rules to follow, here's my best attempt:
1. Only use const
at the top level scope
const HERE = "is okay";
function nested() {
const NOT_HERE = "never seen a reason to";
}
I'm pretty sure I've never seen a binding created in a function that will
actually be upset if it changes. I never use const
inside
nested scopes, and I don't intend to. Why? Because my mom raised me
right (see my usage of semicolons), that's why.
2. Avoid using let
at the top level scope
On the other hand, if you need to use let
at the top-level
scope, that's a sign you have some sort of global singleton state which
could cause you problems.
let globalState = "hmmm...";
function fn() {
globalState = "this seems fragile to test...";
}
And like... that's basically it...
And for the love of all that is not shit, please don't try enforcing this with a linter. Let people communicate what they want to communicate. Ya'll drive me crazy with that shit.
Now get the fuck out of my house.