面試題-手寫凍結一個對象

凍結對象的概念

如果我們定義一個常量,我們現在都會使用 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
}]

更多文章可以看我的博客,如有錯誤,歡迎指正

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章