面试题-手写冻结一个对象

596 阅读3分钟

冻结对象的概念

如果我们定义一个常量,我们现在都会使用 es6 的 const。但是 const 对于引用数据类型,指的是变量对应的指针是常量,但是指针所指向的内容是可以变的,比如下例

const a = 1;
a = 2; // chrome报错:Uncaught TypeError: Assignment to constant variable.

const a = {aa: 1};
a.aa = 2;
console.log(a.aa); // 2

冻结对象,终极的效果是,每个值都不能修改,也不能被删除属性

冻结对象的作用

如果我们编写函数库的时候,特别有时候需要用 es6 的 export 出去一些新的对象,因为此时 es6 的 import/export 是引用,有时候你不希望你的调用者去改变对应的值,这时候就需要把对象冻住

手写冻结一个对象

直接修改 Object.defineProperty

const a = {aa: 1};
Object.defineProperty(a, 'aa', {
  writable: false,
	configurable: false
})
a.aa = 2;
a.bb = 3;
console.log(a);
delete a.aa;
console.log(a)

a 的打印是:

{
	aa: 1,
	bb: 3
}

所以实际上,Object.defineProperty 的 writable 只能将已经定义过的 key“冻住”,对应 key 的值不能修改。但对于新增的属性则无法冻结

Object.preventExtensions()

Object.preventExtentsions()让一个对象不可扩展,就是不能添加新的属性

const a = [1, 2, 3, {second: 11}];
console.log(a);
console.log(Object.getOwnPropertyDescriptor(a, 0));
Object.preventExtensions(a); // 开始锁定对象
a[4] = 4;
console.log(a)
console.log(Object.getOwnPropertyDescriptor(a, '1'));
console.log(Object.isExtensible(a));
delete a[0];
console.log(a);

输出如下:

[1, 2, 3, [object Object] {
  second: 11
}]
[object Object] {
  configurable: true,
  enumerable: true,
  value: 1,
  writable: true
}
[1, 2, 3, [object Object] {
  second: 11
}]
[object Object] {
  configurable: true,
  enumerable: true,
  value: 2,
  writable: true
}
false
[undefined, 2, 3, [object Object] {
  second: 11
}]

由此可见,preventExtensions 可以阻止对象新增属性。但是原对象依旧可以改删其它原有属性,

seal

Object.seal 封闭一个对象,阻止添加新属性并将现有的属性标记为不可配置(效果就是可以更改值,但是不能删除该属性)。

const a = [1, 2, 3, {second: 11}];
console.log(a);
console.log(Object.getOwnPropertyDescriptor(a, 0));
Object.seal(a);
a[4] = 4;
console.log(a);
console.log(Object.getOwnPropertyDescriptor(a, '1'));
console.log(Object.isExtensible(a));
delete a[0];
console.log(a);
a[0] = 5;
console.log(a);

输出如下:

[1, 2, 3, [object Object] {
  second: 11
}]
[object Object] {
  configurable: true,
  enumerable: true,
  value: 1,
  writable: true
}
[1, 2, 3, [object Object] {
  second: 11
}]
[object Object] {
  configurable: false,
  enumerable: true,
  value: 2,
  writable: true
}
false
[1, 2, 3, [object Object] {
  second: 11
}]
[1, 2, 3, [object Object] {
  second: 11
}]

可以看到,configurable 已经变成了 false,说明是不可以删除的,后面的 delete 操作也是无效。但是,对象里面的值却是可以更改的

可以用过 Object.isSealed() 判断这个对象是否被 seal 过

freeze

Object.freese() 可以冻结一个对象,这个是完全冻结,不能添加新属性,不能删除已有属性,不能更改对象已有属性的可枚举性,可配置性,可写性以及不能修改属性的值。该对象的原型也不能被修改

const a = [1, 2, 3];
console.log(a);
Object.freeze(a);
a[0] = 4;
console.log(a)

输出如下

[1, 2, 3]
[1, 2, 3]

由此可见,对象里面的元素也是不可变的

再来试验,如果对应的 value 不是个简单数据类型呢,比如如下

const a = [1, 2, 3, {second: 11}];
console.log(a);
console.log(Object.getOwnPropertyDescriptor(a, 0));
Object.freeze(a);
a[4] = 4;
console.log(a);
console.log(Object.getOwnPropertyDescriptor(a, '1'));
console.log(Object.isExtensible(a));
delete a[0];
console.log(a);

输出如下


[1, 2, 3, [object Object] {
  second: 11
}]
[object Object] {
  configurable: true,
  enumerable: true,
  value: 1,
  writable: true
}
[1, 2, 3, [object Object] {
  second: 11
}]
[object Object] {
  configurable: false,
  enumerable: true,
  value: 2,
  writable: false
}
false
[1, 2, 3, [object Object] {
  second: 11
}]

更多文章可以看我的博客,如有错误,欢迎指正