一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情。
我们使用 const 的时候是没有办法冻结对象的,这问题让开发非常头大,通常我们会使用 Object.freeze() ,其实用 proxy 也是可以实现的。两种方法
问题
js 中的对象类型是可变的,无论是否将它们定义为 const 变量。事实上, const 在定义对象时使用只会防止变量被重新赋值。但可以重新分配 const 对象或数组的属性,如下所示:
const myObj = { a: 10, b: 20, c: 30 };
myObj.a = 12; // { a: 12, b: 20, c: 30 };
const myArr = [15, 25, 35];
myArr[1] = 28; // [15, 28, 35];
Object.freeze()
为了保证对象不可变,我们可以利用 Object.freeze() 来冻结对象上的属性。
Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。
const myObj = {
a: 1,
b: 'hello',
c: [0, 1, 2],
d: { e: 1, f: 2 }
};
Object.freeze(myObj);
myObj.a = 10;
myObj.b = 'hi';
myObj.c[1] = 4;
myObj.d.e = 0;
/*
myObj = {
a: 1,
b: 'hello',
c: [0, 4, 2],
d: { e: 0, f: 2 }
}
*/
对于一个常量对象,整个引用图(直接和间接引用其他对象)只能引用不可变的冻结对象。冻结的对象被认为是不可变的,因为整个对象中的整个对象状态(对其他对象的值和引用)是固定的。注意,字符串,数字和布尔总是不可变的,而函数和数组是对象。
但是上面这种只是冻结了对象,我们还需要使用递归来判断属性是否为一个对象,并且用 Object.isFrozen() 为 false 来判断是否被冻结。
const myObj = {
a: 1,
b: 'hello',
c: [0, 1, 2],
d: { e: 1, f: 2 }
};
const deepFreeze = obj => {
Object.keys(obj).forEach(prop => {
if (typeof obj[prop] === 'object' && !Object.isFrozen(obj[prop])) deepFreeze(obj[prop]);
});
return Object.freeze(obj);
};
deepFreeze(myObj);
myObj.a = 10;
myObj.b = 'hi';
myObj.c[1] = 4;
myObj.d.e = 0;
/*
myObj = {
a: 1,
b: 'hello',
c: [0, 1, 2],
d: { e: 1, f: 2 }
}
*/
如上 我们就可以深度冻结对象了。
对比 Object.seal()
用Object.seal()密封的对象可以改变它们现有的属性。使用Object.freeze() 冻结的对象中现有属性是不可变的。
proxy
const term = {
id: 1,
value: 'hello',
properties: [{ type: 'usage', value: 'greeting' }],
};
const immutable = obj =>
new Proxy(obj, {
get(target, prop) {
return typeof target[prop] === 'object'
? immutable(target[prop])
: target[prop];
},
set() {
throw new Error('This object is immutable.');
},
});
const immutableTerm = immutable(term);
const immutableProperty = immutableTerm.properties[0];
immutableTerm.name = 'hi'; // Error: This object is immutable.
immutableTerm.id = 2; // Error: This object is immutable.
immutableProperty.value = 'pronoun'; // Error: This object is immutable.
这段代码并不难理解,我们用 set() 来让对象不可改变,使用 get() 检查对象的属性,递归为对象的属性,将代理应用于对象的属性。