Making Any Object Fully Immutable In JavaScript
Immutability in JavaScript is an interesting problem. It is easy to make variables unchangeable via const, but if those variables are objects, the object elements can be modified. There are various utilities to help make an object immutable, like Object.freeze, but they have issues too. Object.freeze only applies to the top level properties and does not affect array indices. So the only way is to combine the methods by looping through objects:
function makeImmutable(value) { // The extra instance of handles cases like new Number() if (typeof value === 'object' && !(value instanceof Number) && !(value instanceof String)) { for (let key in value) { value[key] = makeImmutable(value[key]) } return Object.freeze(value); } else { return value; } }; function printError(callback) { // Not a good practice unless you are debugging or showing an example try { callback(); } catch(e) { console.info(e); } } const obj = { property1: 'value1', array1: [ new String('element1'), 'element2', { property2: { property3: [1, 2] } } ] }; const objImmutable = makeImmutable(obj); // Each of the below will through an error in most environments (Some // environments will just not modify the value) printError(() => objImmutable.property1 = 'EDITED'); printError(() => objImmutable.property1[0] = 'EDITED'); printError(() => objImmutable.array1[0] = 'EDITED'); printError(() => objImmutable.array1[2].property2 = 'EDITED'); printError(() => objImmutable.array1[2].property2.property3 = 'EDITED'); printError(() => objImmutable.array1[2].property2.property3[1] = 'EDITED'); printError(() => objImmutable.array1[2].property2.property3[30] = 'EDITED'); // Stringify gaurantees the full depth of the object will be printed console.log(JSON.stringify(objImmutable, null, 2));
In other words start from the lowest level of an object, progressively building immutability. Note that I used arrow functions here to reduce the syntax and show the code flow bwtter. For environments that don't support arrow functions, like IE 11, just replace them with normal functions.
Immutability is useful to reduce errors by reducing the number of ways a system can change. Additionally, the above code can be used to detect when foreign code that you do not know about is modifying any object. With the above a stack trace will tell you exactly where. Finally, although the above covers any case, it is slow for large objects. So it is better to use a library like immutable.js.
Github Location: https://github.com/Jacob-Friesen/obscurejs/blob/master/2017/makeImmutable.js















