JS對象-不可擴展對象、密封對象、凍結對象

JS中的不可擴展對象、密封對象、凍結對象

JavaScript 中,可以對對象的權限進行配置,通過配置,可將對象設置爲不可擴展對象、密封對象、凍結對象等,以達到保護對象屬性的目的。

不可擴展對象

如果一個對象可以添加新的屬性,則這個對象是可擴展的。Object.preventExtensions()將對象標記爲不再可擴展,因此它將永遠不會具有超出它被標記爲不可擴展的屬性。注意,一般來說,不可擴展對象的屬性可能仍然可被刪除。嘗試將新屬性添加到不可擴展對象將靜默失敗或拋出TypeError(最常見但不排除其他情況,如在strict mode中)。
Object.preventExtensions()僅阻止添加自身的屬性。但屬性仍然可以添加到對象原型。
一旦使其不可擴展,就無法再對象進行擴展。

/**
 * 使用Object.preventExtensions()方法來阻止對象擴展
 * 不可擴展對象不能繼續添加新屬性與新方法
 * 可以修改原屬性與原方法
 */

var obj = {
  a: 'a',
  b: ['b']
};

// 通過Object.preventExtensions()來阻止擴展
Object.preventExtensions(obj);

// 嚴格模式下報錯,非嚴格模式下靜默失敗
obj.c = 'c';
console.log(obj);   // { a: 'a', b: [ 'b' ] }

function changeObj() {
  "use strict"
  obj.d = 'd';
  console.log(obj);
} 

try {
  changeObj();
} catch (ex) {
  console.error("error: " + ex.message);    // error: Cannot add property d, object is not extensible
}

// 仍可以修改已有的屬性值
obj.a = 'c';
obj.b.push('b2');
console.log(obj);

// 使用Object.isExtensible()可以檢測對象是否可擴展
var obj2 = {
  name: "obj2"
}
console.log(Object.isExtensible(obj));    // false
console.log(Object.isExtensible(obj2));    // true 

密封對象

密封對象不可擴展,而且已有的屬性成員[[configurable]]特性將被設置成false(意味着不能刪除屬性和方法,但是可修改已有屬性值)

/**
 * 使用Object.seal()可以將對象密封
 * 密封對象不可擴展
 * 已有的屬性成員[[configurable]]特性將被設置成false(意味着不能刪除屬性和方法,但是可修改已有屬性值)
 */

let obj = {
  a: 'a',
  b: ['b']
};

Object.seal(obj);

// 非嚴格模式下靜默失敗
delete obj.a;
obj.c = 'c';
console.log(obj);   // { a: 'a', b: [ 'b' ] }

// 嚴格模式下拋出異常
try {
  try {
    (function add_d() {
      "use strict"
      obj.d = 'd';
    })();
  } catch (ex) {
    console.error("add_d:" + ex.message);   // add_d:Cannot add property d, object is not extensible
  } finally {
    (function delete_b() {
      "use strict"
      delete obj.b;
    })();
  }
} catch (ex) {
  console.error('delete_b: ' + ex.message);   // delete_b: Cannot delete property 'b' of #<Object>
}

// 可以修改屬性值
obj.a = 'a2';
console.log(obj.a);   // 'a2'

// 通過Object.isSealed()方法可以判斷對象是否被密封
let obj2 = {
  name: 'obj2'
};
console.log(Object.isSealed(obj));    // true
console.log(Object.isSealed(obj2));   // false

// 被封閉的對象同時不可擴展
console.log(Object.isExtensible(obj));    // false
console.log(Object.isExtensible(obj2));   // true

凍結對象

最嚴格的防止篡改級別是凍結對象,凍結的對象既不可以擴展,又是密封的,而且對象數據屬性的[[writable]]特性會被設置爲false。 如果定義[[Set]]函數,訪問器屬性仍然是可寫的

/**
 * 使用Object.freeze()方法可以凍結對象
 * 凍結對象(frozen object)既不可擴展,又是密封的,而且對象的[[writable]]特性會被設置爲false
 * 如果定義[[Set]]函數,訪問器屬性仍然是可寫的
 */

let obj = {
  a: 'a',
  b: ['b']
}

let obj2 = {
  name: 'obj2',
  val: 'hello'
}

// 使用set函數定義一個存取描述符
Object.defineProperty(obj, 'set_obj2', {
  configurable: true,
  enumerable: true,
  get: () => {
    return obj2.val;
  },
  set: (val) => {
    obj2.val = val;
  }
})
// 凍結對象
Object.freeze(obj);

// 非嚴格模式下靜默失敗
obj.c = 'c';
console.log(obj.c);   // undefined

delete obj.a;
console.log(obj.a);   // 'a'

obj.a = 'a2';
console.log(obj.a);   // 'a'

// [[set]]函數 訪問器屬性仍然可寫
obj.set_obj2 = 'haha'
console.log(obj.set_obj2);    // 'haha'

// 嚴格模式下拋出異常
try {
  try {
    (function add_d() {
      "use strict"
      obj.d = 'd';
    })();
  } catch (ex) {
    console.error("add_d:" + ex.message);   // add_d:Cannot add property d, object is not extensible
  } finally {
    (function delete_b() {
      "use strict"
      delete obj.b;
    })();
  }
} catch (ex) {
  console.error('delete_b: ' + ex.message);   // delete_b: Cannot delete property 'b' of #<Object>
}

// 仍然修改obj.b
try {
  (function change_b() {
    "use strict"
    obj.b.push('b2');
    console.log(obj.b);   // [ 'b', 'b2' ]
  })();
} catch (ex) {
  console.error('change_b: ' + ex.message);
}

// 使用Object.isFrozen()方法檢測凍結對象
console.log(Object.isFrozen(obj));   // true
console.log(Object.isSealed(obj));   // true
console.log(Object.isExtensible(obj));   // false

console.log(Object.isFrozen(obj2));    // false
console.log(Object.isSealed(obj2));    // false
console.log(Object.isExtensible(obj2));    // true
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章