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. 注意事项
-
Object.freeze()只能冻结对象本身,不能冻结函数或原始类型。 - 深冻结会递归所有嵌套对象,但不会冻结原型链上的属性。
- 如果对象里有循环引用,需要做额外处理,否则会导致死循环。
如果你希望,我可以帮你写一个支持循环引用的安全版深冻结函数,这样即使对象内部有循环也不会报错。
你希望我写吗?
===============================================================================
要支持循环引用的安全版深冻结函数,我们需要避免在递归过程中重复处理已经被冻结的对象。下面是一个实现:
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)检查对象是否已经被冻结,避免重复冻结同一个对象,从而防止死循环。
这样就能安全地处理循环引用的问题,同时保证深冻结的效果。
如果你有其他需求或者问题,随时告诉我!