是否有一种简单的方法可以完全冻结Javascript中的对象及其子对象(深度冻结)?

时间:2019-05-31 22:22:14

标签: javascript arrays object ecmascript-6

通常,您必须传递一个对象作为参数。访问这些对象的函数通过引用传递,并且可以更改原始对象。根据情况可能是不希望的结果。因此,有一种冻结对象的方法。我知道Object.freeze()

但是它不会影响其中的对象/数组。

例如

a = { arr: [1,2,3]}; 
Object.freeze(a);
a.arr.push(4); // still works

6 个答案:

答案 0 :(得分:1)

如果您查看MDN,那里有一个建议使用DeepFreeze功能的函数,但是它并不安全。我个人有一个ES5版本来异步迭代。对于ES6,遵循这些思路的某些方法可能会起作用,但是我并未对其进行全面测试:

function deepFreeze(o,promises,oldestParent){
    promises = promises || [];
    oldestParent = oldestParent || o;
    promises.push(
        Promise.resolve().then(function(){
            Object.values(Object.freeze(o)).forEach(function(d,i){
                typeof d === "object" && deepFreeze(d,promises,oldestParent);
            });
            return oldestParent;
        })
    );
    return Promise.all(promises).then((a)=>a[0]);
}

var o = {a:3,b:{test:1,test2:2},c:1};
deepFreeze(o).then(function(x){console.log(x)}); //o is deep frozen

警告:我假设您对象的属性是可枚举的,否则请使用getOwnPropertyNames

答案 1 :(得分:1)

您可以像这样创建一个非常简单的递归解决方案:

let a = {
  arr: [1, 2, 3],
  name: "A. Bc"
};

const deepFreeze = o => {
  for (let [key, value] of Object.entries(o)) {
    if (o.hasOwnProperty(key) && typeof value == "object") {
      deepFreeze(value);
    }
  }
  Object.freeze(o);
  return o;
}

deepFreeze(a);

try {
  a.arr.push(4);
} catch(e) {
  console.log("Error: ", e);
}
console.log(a);

答案 2 :(得分:0)

检出deep-freeze会在对象上递归Object.freeze()

这是他们的实现方式

function deepFreeze (o) {
  Object.freeze(o);

  Object.getOwnPropertyNames(o).forEach(function (prop) {
    if (o.hasOwnProperty(prop)
    && o[prop] !== null
    && (typeof o[prop] === "object" || typeof o[prop] === "function")
    && !Object.isFrozen(o[prop])) {
      deepFreeze(o[prop]);
    }
  });

  return o;
};

答案 3 :(得分:0)

function deepFreeze(object) {

  // Retrieve the property names defined on object
  var propNames = Object.getOwnPropertyNames(object);

  // Freeze properties before freezing self

  for (let name of propNames) {
    let value = object[name];

    object[name] = value && typeof value === "object" ? 
      deepFreeze(value) : value;
  }

  return Object.freeze(object);
}

let a = { arr: [1,2,3]}; 
deepFreeze(a);
a.arr.push(4); // TypeError: Cannot add property 3, object is not extensible

(第二个代码[第二个灰色区域])https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze#What_is_shallow_freeze

谢谢:)

答案 4 :(得分:0)

要深度冻结所有可枚举的属性(ES2015 +):

// Recursively freeze an object
const deepFreeze = x => {
  Object.freeze(x)
  Object.values(x).forEach(deepFreeze)
}

如果您有循环引用:

// Recursively freeze an object with circular references
const deepFreeze = x => {
  Object.freeze(x)
  Object.values(x).filter(x => !Object.isFrozen(x)).forEach(deepFreeze)
}

如果您还必须冻结任何浅冷冻的东西(速度稍慢):

// Recursively deep freeze an object with circular references
const deepFreeze = (x, frozen = []) => {
  if (frozen.includes(x)) return null
  frozen.push(x)
  Object.freeze(x)
  Object.values(x).forEach(x => deepFreeze(x, frozen))
}

tldr;两线:

const deepFreeze = (x, frozen = []) => frozen.includes(x) ||
  frozen.push(Object.freeze(x)) && Object.values(x).forEach(x => deepFreeze(x, frozen))

答案 5 :(得分:0)

// when we iterate over obj keys, make sure keys that their value is object are passed
const isObject=(val)=>val && typeof val==='object'

function deepFreeze(obj){
   // if object is already frozen, skip it
   if (isObject(obj) && !Object.isFrozen(obj)){
       Object.keys(obj).forEach((key)=>deepFreeze(obj[key]))
   }
return obj
}