Javascript Immutability
To understand the following principe we will see the difference between var / let / const and the scope property about why now everyone is using let or const instead of the good old var.
Then we will see the immutability problem about using let or const and why most of the time const is possibly mutable !
The scope problem
var
var was the first element to permit using variable in javascript but a lot of things made him not good as expected and this is why people was happy when let/const have been released.
Declaring a var element can be annoying because of the scope effect behavior created by var.
Following this exemple :
var x = 1;if (x === 1) {var x = 2;console.log(x); // expect 2 here}console.log(x); // but here are we expecting 1 or 2 ?
So the last console.log will not show "1" but he will show 2 because using var make him available everywhere, so when you call :
var x = 1;if (x === 1) {var x = 2;}
In fact is like calling this :
var x = 1;if (x === 1) {x = 2;}// or likevar x;x = 1;if (x === 1) {x = 2;}
You will modify the value of x instead of creating a new variable that will permit you to have a scoped element that can't be modified outside the if this is what we call Hoisting.
let
let is the element introduce to permit being used like var but with a good scope declaration, what does this mean ?
It's pretty simple, every time you open closure {} and use let inside, the variable can't be used outside of this closure.
Let's take the last exemple replacing var by let :
let x = 1;if (x === 1) {let x = 2;console.log(x); // expect 2 here}console.log(x); // but here are we expecting 1 or 2 ?
Now the last console.log(x) will no more show 2 but now it will show properly 1 as expected, as I said let work well with closure, he will create a new reference every time you call it inside some closure.
And if you try to redeclare x like this following :
let x = 1;if (x === 1) {let x = 2;console.log(x); // expect 2 herelet x = 3; // Getting : Error: Identifier 'x' has already been declaredconsole.log(x);}console.log(x);
You got an error, because you can't redeclare x inside a scope where he's already declared.
const
const is the element that introduce a "constant" mode for some variable, you can't change the value of a constant or change his signature or his type.
const x = 1;if (x === 1) {x = 2; // error}console.log(x);
This will simple send an error asking you to transform const to let or to not change the value of x
Aswell you can't change the type of the variable :
const x = 1;if (x === 1) {x = "hello"; // error}console.log(x);
What about the immutability ?
Immutability is the fact that you can't change the value or the signature of a variable, like const.
But the problem with const is that you can make a thing worked that shouldn't like this :
const x = {};x.hello = "hello";console.log(x); // {hello: "hello"}
As you can see, I can add an element to my object and the same goes for list :
const x = [];x["hello"] = "hello";console.log(x); // [hello: "hello"]
That's a problem no ? :) You can still change the value of the list or the object because const just check that you can't reasigne an element to this variable.
Resolving the problem
To resolve this problem, you can use a native javascript solution, like Object.freeze():
const obj = {prop: 42,};Object.freeze(obj);obj.prop = 33; // Throws an error in strict modeconsole.log(obj.prop); // expected output: 42
The last console.log() will show 42 and not 33 like previously, it will freeze the object in order to make him completlty immutable. But it cost performance if you use a lot of Object.freeze().
To prevent this performance problem, you can use some specific language as Flow or Typescript.
You can also use some library like immer or immutableJS