27

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)

1

5 Answers 5

31
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

Sign up to request clarification or add additional context in comments.

7 Comments

o.hasOwnProperty(prop) makes no sense
I mean that this check doesn't make any sense when iterating the own property names of an object.
It makes as much sense as if( true && ... it doesn't hurt anything, but it certainly isn't a useful condition.
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 undefined
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/…
|
8

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.

3 Comments

Fully tested TypeScript implementation: gist.github.com/tkrotoff/e997cd6ff8d6cf6e51e6bb6146407fc3
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;
ReadOnly is for knowing about compile time, but freeze is about runtime.
-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 } };

1 Comment

I believe OP meant to ask how to freeze all nested objects on all levels, not only direct child nodes.
-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.

1 Comment

Will fail on deepFreeze({ a: null })
-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

1 Comment

Interesting approach for Vue applications.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.