Object構造函數(二)

3、對象的設置

(1)Object.isExtensible()和Object.preventExtensions() ES5

Object.isExtensible() 方法判斷一個對象是否是可擴展的(是否可以在它上面添加新的屬性)。返回一個Boolean值。

默認情況下,對象是可擴展的:即可以爲他們添加新的屬性。以及它們的 __proto__ 屬性可以被更改。Object.preventExtensionsObject.seal 或 Object.freeze 方法都可以標記一個對象爲不可擴展(non-extensible)

 新對象默認是可擴展的.
var empty = {};
Object.isExtensible(empty);  === true

 ...可以變的不可擴展.
Object.preventExtensions(empty);
Object.isExtensible(empty);  === false

 密封對象是不可擴展的.
var sealed = Object.seal({});
Object.isExtensible(sealed);  === false

凍結對象也是不可擴展.
var frozen = Object.freeze({});
Object.isExtensible(frozen);  === false

注意:

在 ES5 中,如果參數不是一個對象類型,將拋出一個 TypeError 異常。

在 ES6 中, non-object 參數將被視爲一個不可擴展的普通對象,因此會返回 false 。 

Object.isExtensible(1);
TypeError: 1 is not an object (ES5 code)

Object.isExtensible(1);
false                         (ES6 code)

 Object.preventExtensions()方法讓一個對象變的不可擴展,也就是永遠不能再添加新的屬性。返回已經不可擴展的該對象。

如果一個對象可以添加新的屬性,則這個對象是可擴展的。Object.preventExtensions()將對象標記爲不再可擴展,這樣它將永遠不會具有它被標記爲不可擴展時持有的屬性之外的屬性。注意,一般來說,不可擴展對象的屬性可能仍然可被刪除。嘗試將新屬性添加到不可擴展對象將靜默失敗或拋出TypeError(最常見的情況是strict mode中,但不排除其他情況)。

Object.preventExtensions()僅阻止添加自身的屬性。但其對象類型的原型依然可以添加新的屬性。

該方法使得目標對象的 [[prototype]]  不可變;任何重新賦值 [[prototype]] 操作都會拋出 TypeError 。這種行爲只針對內部的 [[prototype]] 屬性, 目標對象的其它屬性將保持可變。

一旦將對象變爲不可擴展的對象,就再也不能使其可擴展。

在 ES5 中,如果參數不是一個對象類型(而是原始類型),將拋出一個TypeError異常。在 ES2015 中,非對象參數將被視爲一個不可擴展的普通對象,因此會被直接返回。

(2)Object.isFrozen()和Object.freeze()  ES5  IE9

Object.isFrozen()方法判斷一個對象是否被凍結。返回Boolean值。

一個對象是凍結的是指它不可擴展,所有屬性都是不可配置的,且所有數據屬性(即沒有getter或setter組件的訪問器的屬性)都是不可寫的。

// 一個對象默認是可擴展的,所以它也是非凍結的.
Object.isFrozen({}); // === false

// 一個不可擴展的空對象同時也是一個凍結對象.
var vacuouslyFrozen = Object.preventExtensions({});
Object.isFrozen(vacuouslyFrozen) //=== true;

// 一個非空對象默認也是非凍結的.
var oneProp = { p: 42 };
Object.isFrozen(oneProp) //=== false

// 讓這個對象變的不可擴展,並不意味着這個對象變成了凍結對象,
// 因爲p屬性仍然是可以配置的(而且可寫的).
Object.preventExtensions(oneProp);
Object.isFrozen(oneProp) //=== false

// 此時,如果刪除了這個屬性,則它會成爲一個凍結對象.
delete oneProp.p;
Object.isFrozen(oneProp) //=== true

// 一個不可擴展的對象,擁有一個不可寫但可配置的屬性,則它仍然是非凍結的.
var nonWritable = { e: "plep" };
Object.preventExtensions(nonWritable);
Object.defineProperty(nonWritable, "e", { writable: false }); // 變得不可寫
Object.isFrozen(nonWritable) //=== false

// 把這個屬性改爲不可配置,會讓這個對象成爲凍結對象.
Object.defineProperty(nonWritable, "e", { configurable: false }); // 變得不可配置
Object.isFrozen(nonWritable) //=== true

// 一個不可擴展的對象,擁有一個不可配置但可寫的屬性,則它仍然是非凍結的.
var nonConfigurable = { release: "the kraken!" };
Object.preventExtensions(nonConfigurable);
Object.defineProperty(nonConfigurable, "release", { configurable: false });
Object.isFrozen(nonConfigurable) //=== false

// 把這個屬性改爲不可寫,會讓這個對象成爲凍結對象.
Object.defineProperty(nonConfigurable, "release", { writable: false });
Object.isFrozen(nonConfigurable) //=== true

// 一個不可擴展的對象,值擁有一個訪問器屬性,則它仍然是非凍結的.
var accessor = { get food() { return "yum"; } };
Object.preventExtensions(accessor);
Object.isFrozen(accessor) //=== false

// ...但把這個屬性改爲不可配置,會讓這個對象成爲凍結對象.
Object.defineProperty(accessor, "food", { configurable: false });
Object.isFrozen(accessor) //=== true

// 使用Object.freeze是凍結一個對象最方便的方法.
var frozen = { 1: 81 };
Object.isFrozen(frozen) //=== false
Object.freeze(frozen);
Object.isFrozen(frozen) //=== true

// 一個凍結對象也是一個密封對象.
Object.isSealed(frozen) //=== true

// 當然,更是一個不可擴展的對象.
Object.isExtensible(frozen) //=== false

 在 ES5 中,如果參數不是一個對象類型,將拋出一個TypeError異常。在 ES2015 中,非對象參數將被視爲一個凍結的普通對象,因此會返回true

Object.freeze() 方法可以凍結一個對象。

一個被凍結的對象再也不能被修改;凍結了一個對象則不能向這個對象添加新的屬性,不能刪除已有屬性,不能修改該對象已有屬性的可枚舉性、可配置性、可寫性,以及不能修改已有屬性的值。

此外,凍結一個對象後該對象的原型也不能被修改。freeze() 返回和傳入的參數相同的對象。這個方法返回傳遞的對象,而不是創建一個被凍結的副本。

被凍結對象自身的所有屬性都不可能以任何方式被修改。任何修改嘗試都會失敗,無論是靜默地還是通過拋出TypeError異常(最常見但不僅限於strict mode)。

數據屬性的值不可更改,訪問器屬性(有getter和setter)也同樣(但由於是函數調用,給人的錯覺是還是可以修改這個屬性)。如果一個屬性的值是個對象,則這個屬性對象中的屬性是可以修改的,除非它也是個凍結對象。數組作爲一種對象,被凍結,其元素不能被修改。沒有數組元素可以被添加或移除。

深凍結對象:

// 深凍結函數.
function deepFreeze(obj) {

  // 取回定義在obj上的屬性名
  var propNames = Object.getOwnPropertyNames(obj);

  // 在凍結自身之前凍結屬性
  propNames.forEach(function(name) {
    var prop = obj[name];

    // 如果prop是個對象,凍結它
    if (typeof prop == 'object' && prop !== null)
      deepFreeze(prop);
  });

  // 凍結自身(no-op if already frozen)
  return Object.freeze(obj);
}

obj2 = {
  internal: {}
};

deepFreeze(obj2);
obj2.internal.a = 'anotherValue';
obj2.internal.a; // undefined

在ES5中,如果這個方法的參數不是一個對象(一個原始值),那麼它會導致 TypeError。在ES2015中,非對象參數將被視爲要被凍結的普通對象,並被簡單地返回。

Object.seal()密封的對象可以改變它們現有的屬性。使用Object.freeze() 凍結的對象中現有屬性是不可變的

(3)Object.isSealed()和Object.seal()  兼容性ES5 IE9

Object.isSealed() 方法判斷一個對象是否被密封。返回Boolean值。

如果這個對象是密封的,則返回 true,否則返回 false。密封對象是指那些不可 擴展 的,且所有自身屬性都不可配置且因此不可刪除(但不一定是不可寫)的對象。

// 新建的對象默認不是密封的.
var empty = {};
Object.isSealed(empty); // === false

// 如果你把一個空對象變的不可擴展,則它同時也會變成個密封對象.
Object.preventExtensions(empty);
Object.isSealed(empty); // === true

// 但如果這個對象不是空對象,則它不會變成密封對象,因爲密封對象的所有自身屬性必須是不可配置的.
var hasProp = { fee: "fie foe fum" };
Object.preventExtensions(hasProp);
Object.isSealed(hasProp); // === false

// 如果把這個屬性變的不可配置,則這個對象也就成了密封對象.
Object.defineProperty(hasProp, "fee", { configurable: false });
Object.isSealed(hasProp); // === true

// 最簡單的方法來生成一個密封對象,當然是使用Object.seal.
var sealed = {};
Object.seal(sealed);
Object.isSealed(sealed); // === true

// 一個密封對象同時也是不可擴展的.
Object.isExtensible(sealed); // === false

// 一個密封對象也可以是一個凍結對象,但不是必須的.
Object.isFrozen(sealed); // === true ,所有的屬性都是不可寫的
var s2 = Object.seal({ p: 3 });
Object.isFrozen(s2); // === false, 屬性"p"可寫

var s3 = Object.seal({ get p() { return 0; } });
Object.isFrozen(s3); // === true ,訪問器屬性不考慮可寫不可寫,只考慮是否可配置

Object.seal()方法封閉一個對象,阻止添加新屬性並將所有現有屬性標記爲不可配置。當前屬性的值只要原來是可寫的就可以改變。返回被密封的對象,即被密封對象的引用。

通常,一個對象是可擴展的(可以添加新的屬性)。密封一個對象會讓這個對象變的不能添加新屬性,且所有已有屬性會變的不可配置。屬性不可配置的效果就是屬性變的不可刪除,以及一個數據屬性不能被重新定義成爲訪問器屬性,或者反之。但屬性的值仍然可以修改。因爲writable還是原本設置的,可能是true。數據屬性的writable由定義時決定,普通的屬性writable是true。

嘗試刪除一個密封對象的屬性或者將某個密封對象的屬性從數據屬性轉換成訪問器屬性都會失敗,拋出異常。

不會影響從原型鏈上繼承的屬性。但 __proto__(該屬性還不是在所有瀏覽器通用的) 屬性的值也會不能修改。

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