Objects in JavaScript are mutable, regardless if you define them as
const
variables or not. In fact, using
const
when defining an object only prevents the variable from
being reassigned. However, you can reassign the properties of a
const
object or array, like this:
const myObj = { a: 10, b: 20, c: 30 };
.a = 12; // { a: 12, b: 20, c: 30 };
myObj
const myArr = [15, 25, 35];
1] = 28; // [15, 28, 35]; myArr[
To make an object immutable, we can utilize Object.freeze()
,
which will prevent the addition of new properties and prevent deletion and
changes to existing properties to some extent. However, while
Object.freeze()
provides somewhat of a solution, it only
mitigates the problem to the next nesting level, as in reality it performs
a shallow freeze. This means that properties that are objects or arrays
can still be mutated:
const myObj = {
a: 1,
b: 'hello',
c: [0, 1, 2],
d: { e: 1, f: 2 }
;
}Object.freeze(myObj);
.a = 10;
myObj.b = 'hi';
myObj.c[1] = 4;
myObj.d.e = 0;
myObj/*
myObj = {
a: 1,
b: 'hello',
c: [0, 4, 2],
d: { e: 0, f: 2 }
}
*/
As you can see, Object.freeze()
is a step in the right
direction, but only shallow freezes the object. To solve the issue we can
use recursion, checking if each property is itself an object and, if
Object.isFrozen()
is false
, apply
Object.freeze()
to it:
const myObj = {
a: 1,
b: 'hello',
c: [0, 1, 2],
d: { e: 1, f: 2 }
;
}
const deepFreeze = obj => {
Object.keys(obj).forEach(prop => {
if (typeof obj[prop] === 'object' && !Object.isFrozen(obj[prop])) deepFreeze(obj[prop]);
;
})return Object.freeze(obj);
;
}deepFreeze(myObj);
.a = 10;
myObj.b = 'hi';
myObj.c[1] = 4;
myObj.d.e = 0;
myObj
/*
myObj = {
a: 1,
b: 'hello',
c: [0, 1, 2],
d: { e: 1, f: 2 }
}
*/
In the above example, we apply the techniques we described previously to ensure that the given object is deeply frozen. You can view the complete code, along with more examples in the deepFreeze snippet.