通常您需要将对象作为参数传递。访问这些对象的函数是按引用传递的,可以更改原始对象。这取决于情况,可能是不希望的结果。那么有没有一种方法可以冻结对象?我知道有 Object.freeze()
。
但它不会影响其中的对象/数组。
例如
Original Answer翻译成"最初的回答"
a = { arr: [1,2,3]};
Object.freeze(a);
a.arr.push(4); // still works
通常您需要将对象作为参数传递。访问这些对象的函数是按引用传递的,可以更改原始对象。这取决于情况,可能是不希望的结果。那么有没有一种方法可以冻结对象?我知道有 Object.freeze()
。
但它不会影响其中的对象/数组。
例如
Original Answer翻译成"最初的回答"
a = { arr: [1,2,3]};
Object.freeze(a);
a.arr.push(4); // still works
要将所有可枚举属性冻结(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))
}
const deepFreeze = (x, frozen = []) => frozen.includes(x) ||
frozen.push(Object.freeze(x)) && Object.values(x).forEach(x => deepFreeze(x, frozen))
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
代替。
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
谢谢 :)
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);
另一种方法是使用代理。而不是深度冻结对象,您会得到一个代理对象,禁止写入:
const immutableProxy = o => {
if (o===null || typeof o !== 'object') return o
return new Proxy(o, {
get(obj, prop) {
return immutableProxy(obj[prop])
},
set(obj, prop) {
throw new Error(`Can not set prop ${prop} on immutable object`)
}
})
}
结账 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;
};
const a = produce({ arr: [1,2,3] }, () => {})
() => {}
是必要的,因为 produce
需要一个能够改变第一个参数的函数,但这里并不需要进行任何改变。
如果你只是想冻结一些数据结构,并且不会在其他地方使用不可变性,那么使用额外的第三方库可能并不值得。上述解决方案已经足够了。