JS 深冻结

MDN

function deepFreeze(object) {
  // 获取对象的属性名
  const propNames = Reflect.ownKeys(object);

  // 冻结自身前先冻结属性
  for (const name of propNames) {
    const value = object[name];

    if ((value && typeof value === "object") || typeof value === "function") {
      deepFreeze(value);
    }
  }

  return Object.freeze(object);
}

const obj2 = {
  internal: {
    a: null,
  },
};

deepFreeze(obj2);

obj2.internal.a = "anotherValue"; // 非严格模式下会静默失败
obj2.internal.a; // null

你提到的 “JS 深冻结”,我理解你是想对对象进行递归冻结(deep freeze),不仅冻结对象本身的属性,还要冻结对象中嵌套的对象。普通的 Object.freeze() 只会冻结第一层属性,嵌套对象仍然可以被修改。

下面我给你一个完整的示例:


1. 基本的深冻结函数

function deepFreeze(obj) {
  // 首先冻结对象自身
  Object.freeze(obj);

  // 遍历对象的每个属性
  Object.keys(obj).forEach(key => {
    const prop = obj[key];

    // 如果属性是对象,并且没有被冻结,则递归冻结
    if (typeof prop === 'object' && prop !== null && !Object.isFrozen(prop)) {
      deepFreeze(prop);
    }
  });

  return obj;
}

2. 使用示例

const data = {
  name: 'Alice',
  details: {
    age: 25,
    address: {
      city: 'Beijing'
    }
  }
};

deepFreeze(data);

data.name = 'Bob';               // 不会生效
data.details.age = 30;           // 不会生效
data.details.address.city = 'Shanghai'; // 不会生效

console.log(data);

3. 注意事项

  1. Object.freeze() 只能冻结对象本身,不能冻结函数或原始类型。
  2. 深冻结会递归所有嵌套对象,但不会冻结原型链上的属性。
  3. 如果对象里有循环引用,需要做额外处理,否则会导致死循环。

如果你希望,我可以帮你写一个支持循环引用的安全版深冻结函数,这样即使对象内部有循环也不会报错。

你希望我写吗?

===============================================================================

要支持循环引用的安全版深冻结函数,我们需要避免在递归过程中重复处理已经被冻结的对象。下面是一个实现:

1. 支持循环引用的深冻结函数

function deepFreezeSafe(obj, frozenObjects = new Set()) {
  // 如果对象已经被冻结,直接返回
  if (frozenObjects.has(obj)) return obj;

  // 标记当前对象为已冻结
  frozenObjects.add(obj);

  // 冻结对象本身
  Object.freeze(obj);

  // 遍历对象的每个属性
  Object.keys(obj).forEach(key => {
    const prop = obj[key];

    // 如果属性是对象,并且没有被冻结,则递归冻结
    if (typeof prop === 'object' && prop !== null && !Object.isFrozen(prop)) {
      deepFreezeSafe(prop, frozenObjects);
    }
  });

  return obj;
}

2. 使用示例

const data = {
  name: 'Alice',
  details: {
    age: 25,
    address: {
      city: 'Beijing'
    }
  }
};

data.details.address.self = data.details.address; // 循环引用

deepFreezeSafe(data);

data.name = 'Bob';               // 不会生效
data.details.age = 30;           // 不会生效
data.details.address.city = 'Shanghai'; // 不会生效
data.details.address.self.city = 'Shenzhen'; // 不会生效

console.log(data);

3. 解释

  • frozenObjects: 使用一个 Set 来存储已经被冻结的对象。这样可以避免在遇到循环引用时进入死循环。
  • 递归冻结: 每个对象的嵌套属性会递归调用 deepFreezeSafe 函数进行冻结。如果属性是对象且没有被冻结,就递归冻结。
  • 循环引用: 通过 frozenObjects.has(obj) 检查对象是否已经被冻结,避免重复冻结同一个对象,从而防止死循环。

这样就能安全地处理循环引用的问题,同时保证深冻结的效果。

如果你有其他需求或者问题,随时告诉我!

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 1. 测试对象 2. 被冻结对象特点 【不能】添加新的属性 【不能】删除已有属性 【不能】修改已有属性的值 【不...
    Lawson刘阅读 3,826评论 0 0
  • 王国维在《人间词话》里谈到了治学经验,他说:“古今之成大事业、大学问者,必经过三种之境界。” 巧合的是,最近受 g...
    LucasHC阅读 5,885评论 1 5
  • 一、什么是闭包?闭包有哪些实际运用场景?闭包是如何产生的?闭包产生的变量如何被回收? 二、前端错误如何捕获,pro...
    Benzic阅读 1,202评论 0 0
  • 我们之前已经讨论了对象,现在来全面总结JavaScript的数据类型以及相关问题。 JavaScript数据类型分...
    sunflower_07阅读 1,054评论 0 0
  • Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对...
    小棋子js阅读 4,709评论 0 0

友情链接更多精彩内容