How to freeze nested objects in JavaScript?
Asked Answered
L

5

26

I just stumbled upon Object.freeze() function. It seems to be a pretty good feature but how to make whole object (including nested objects) immutable?

For example I can change innerProp here:

const obj = { prop: { innerProp: 1 } };
obj.prop.innerProp = 5;
console.log(obj.prop.innerProp); // 5

Is it possible do freeze nested objects too? (ECMAScript 5/6)

Lashawna answered 13/1, 2016 at 20:55 Comment(1)
L
28
function deepFreeze (o) {
  Object.freeze(o);
  if (o === undefined) {
    return o;
  }
  
  Object.getOwnPropertyNames(o).forEach(function (prop) {
    if (o[prop] !== null
    && (typeof o[prop] === "object" || typeof o[prop] === "function")
    && !Object.isFrozen(o[prop])) {
      deepFreeze(o[prop]);
    }
  });
  
  return o;
};

https://www.npmjs.com/package/deep-freeze

It's public domain so you don't have to give credit :D

Loma answered 13/1, 2016 at 21:2 Comment(7)
o.hasOwnProperty(prop) makes no senseInsipience
I mean that this check doesn't make any sense when iterating the own property names of an object.Insipience
It makes as much sense as if( true && ... it doesn't hurt anything, but it certainly isn't a useful condition.Cutoff
deepFreeze(undefined) fails since getOwnPropertyNames() expect an objectAcetone
The deepFreeze fn is bad. Why check if o is undefined after it has been passed to Object.freeze? And why not use o == null instead of o === undefined as then the condition will be true for null and undefinedCiccia
404 on the link: github.com/substack/deep-freeze I've seen the project in npm but seems not to be available anymore on Github, MDN provides a similar example: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Alleviative
This solution will work incorrectly if some of the objects in the structure were already frozen before deepFreeze() was called.Damian
A
5

The function can be made much more terse in TS and ES2015.

import type { ReadonlyDeep } from 'type-fest';

export function deepFreeze<T>(o: T) {
  Object.values(o).forEach(v => Object.isFrozen(v) || deepFreeze(v));
  return Object.freeze(o) as ReadonlyDeep<T>;
}
  • Object.isFrozen now returns true for primitive objects.
  • Object.values instead of getOwnPropertyNames.
Astrosphere answered 21/10, 2021 at 4:46 Comment(3)
Fully tested TypeScript implementation: gist.github.com/tkrotoff/e997cd6ff8d6cf6e51e6bb6146407fc3Sevigny
This works for "nice behaved" objects, but (apart from being broken in ES5, where Object.isFrozen(v) throws error if not an object), it blows up if the object has loops let o1 = { a: 1 }; let o2 = { b: 2, ox2: o1}; o1.ox1 = o2; Microtone
ReadOnly is for knowing about compile time, but freeze is about runtime.Emphysema
S
-1
const obj = { prop: { innerProp: 1 } };

Object.freeze(obj);
for(let key in obj) {
   if(obj.hasOwnProperty(key) && typeof obj[key] === 'object') {
      Object.freeze(obj[key]);
   }
}
obj.prop.innerProp = 5;
console.log(obj); // const obj = { prop: { innerProp: 1 } };
Sutler answered 12/4, 2020 at 10:51 Comment(1)
I believe OP meant to ask how to freeze all nested objects on all levels, not only direct child nodes.Damian
D
-1

One can freeze an object using recursive approach.

const deepFreeze = (param) => {
    Object.keys(param).forEach((val) => {
      if(typeof param[val] === "object"){
        deepFreeze(param[val]);
      }
    });
    return Object.freeze(param);
}

const obj = {
  name: "Vidhi",
  position: "Software Developer",
  travelling: {
    europe:"Rome",
    asia:"Tokyo",
  },
}

deepFreeze(obj);

obj.travelling.asia = "New York";  // cannot assign to read only property "asia" of obj
console.log(obj)

One can see in console that object's value has not changed and it will give error if we use strict mode in javascript.

Disoblige answered 11/8, 2023 at 7:20 Comment(1)
Will fail on deepFreeze({ a: null })Damian
R
-3
import { readonly } from 'vue/reactive'

let obj = { x: 1 }

obj = readonly(obj)

This will create a readonly proxy on the object and changes will have no effect. You can use the reference as a usual object.

You can use only reactive part of the vue 3, it's small import actually, but has lots of useful functionality

Re answered 14/2, 2022 at 21:35 Comment(1)
Interesting approach for Vue applications.Akeylah

© 2022 - 2024 — McMap. All rights reserved.