JavaScript中對象的屬性類型

JavaScript中,對象的屬性有兩種:數據屬性和訪問器屬性。javascript

數據屬性

特性:

數據屬性包括一個數據值的位置。在這個位置能夠讀取和寫入值。數據屬性有4個特性。java

  • [[configurable]]:可配置。表示可否刪除屬性、可否修改屬性的特性、可否把屬性變爲訪問器屬性。
  • [[enumerable]]:可枚舉。表示可否經過for-in循環進行遍歷。
  • [[writable]]:可寫。表示可否修改屬性的值。
  • [[value]]:表示這個屬性的數據值。
設置:

ES5提供了Object.defineProperty()方法,來設置屬性的默認特性。該方法接收三個參數:屬性所在對象、屬性名字、屬性描述符對象。其中,描述符對象用來設置configurable、enumerable、writable、value中的一個或多個值。數組

ES5也提供了Object.defineProperties方法,能夠定義多個屬性。函數

  • 在設置了屬性的特性後,任未嘗試超過configurable或writable權限的操做,在非嚴格模式下將會被忽略,在嚴格模式下將會拋出錯誤。
  • 一旦把屬性定義爲configurable: false,就不能再變回configurable: true了。不然,不管是否處於嚴格模式,都會拋出TypeError錯誤。
  • 例外:即使屬性是foncigurable: false,仍然能夠把writable的狀態由true改成false,可是沒法由false改成true。
var person = {};
Object.defineProperty(person, 'name', {
    configurable: false,
    writable: false,
    value: 'Nicholas'
});

// 測試:刪除屬性
delete person.name;  // 因爲configurable: false,刪除失敗
console.log(person.name);  // Nicholas

// 測試:修改特性
Object.defineProperty(person, 'name', {
    configurable: true
});  // 拋出錯誤:TypeError: Cannot redefine property: name

// 測試:修改屬性的值
person.name = 'Greg';  // 因爲writable: false,修改失敗
console.log(person.name);  // Nicholas
  • 結合configurable: falsewritable: false,就能夠建立一個真正的常量屬性。
  • Object.seal()方法,會在一個現有對象上調用Object.preventExtensions(),並把全部屬性標記爲configurable: false
  • Object.freeze()方法,會在一個現有對象上調用Object.seal(),並把全部屬性標記爲writable: false
讀取:

ES5提供了Object.getOwnPropertyDescriptor()方法,來取得給定屬性的描述符。該方法接收兩個參數:屬性所在對象、屬性名字。返回值是一個對象,其屬性有configurable、enumerable、writable、value。測試

還有一種區分枚舉屬性enumerable的方法是propertyIsEnumerable()。該方法不檢查原型鏈。this

  • 在調用Object.defineProperty()方法時,若是不指定,configurable、enumerable、writable的默認值爲false。
  • 對於直接定義在對象上的屬性,configurable、enumerable、writable的默認值爲true,value的值爲undefined。
// 使用Object.definedProperty設置的屬性
var descriptor = Object.getOwnPropertyDescriptor(person, 'name');
console.log(descriptor.configurable);  // false
console.log(descriptor.enumerable);  // false
console.log(descriptor.writable);  // false
console.log(descriptor.value);  // Nicholas

// 直接定義在對象上的屬性
var person2 = { name: "Greg" };
var descriptor2 = Object.getOwnPropertyDescriptor(person2, 'name');
console.log(descriptor2.configurable);  // true
console.log(descriptor2.enumerable);  // true
console.log(descriptor2.writable);  // true
console.log(descriptor2.value);  // Greg

// propertyIsEnumerable()方法
console.log(person.propertyIsEnumerable('name'));  // false
console.log(person2.propertyIsEnumerable('name'));  // true
關於可枚舉屬性
  • Object.keys(obj)會返回一個數組,包含全部可枚舉屬性。不檢查原型鏈。
  • Object.getOwnPropertyNames(obj)會返回一個數組,包含全部屬性,不管是否枚舉。不檢查原型鏈。
  • prop in obj會返回一個布爾值,判斷對象的屬性是否存在,不管是否枚舉。檢查原型鏈。
  • obj.hasOwnProperty(prop)會返回一個布爾值,判斷對象的指定屬性是否存在,不管是否枚舉。不會檢查原型鏈。
  • 不可枚舉的屬性,不會出如今for-in循環的遍歷中。
var myObject = {};
Object.defineProperty(myObject, 'a', {
    enumerable: true,
    value: 2
});
Object.defineProperty(myObject, 'b', {
    enumerable: false,
    value: 3
});

// propertyIsEnumerable
console.log(myObject.propertyIsEnumerable('a')); // true
console.log(myObject.propertyIsEnumerable('b')); // false

// Object.keys()
console.log(Object.keys(myObject));  // ["a"]

// Object.getOwnPropertyNames()
Object.getOwnPropertyNames(myObject);  // ["a", "b"]

// in操做符
console.log('a' in myObject);  // true
console.log('b' in myObject);  // true
console.log('toString' in myObject);  // true

// hasOwnProperty()
console.log(myObject.hasOwnProperty('a'));  // true
console.log(myObject.hasOwnProperty('b'));  // true
console.log(myObject.hasOwnProperty('toString'));  // false

// 遍歷
for (var k in myObject) {
    console.log( k, myObject[k] );
}  // a 2

訪問器屬性

特性:

訪問器屬性有4個特性。對於訪問器屬性,JavaScript會忽略它們的writable和value特性,取而代之的該關心get和set屬性。設計

  • [[configurable]]:表示可否刪除屬性、可否修改屬性的特性、可否把屬性變爲訪問器屬性。
  • [[enumerable]]:表示可否經過for-in循環進行遍歷。直接定義在對象上的屬性,默認值爲true。
  • [[get]]:讀取屬性時調用的函數。直接定義在對象上的屬性,默認值爲undefined。
  • [[set]]:寫入屬性時調用的函數。直接定義在對象上的屬性,默認值爲undefined。
設置:

一樣使用Object.defineProperty()方法和Object.defineProperties方法設置訪問其屬性。code

  • 不必定非要同時指定getter和setter函數。
  • 只指定getter函數意味着屬性不能寫。嘗試寫入屬性,在非嚴格模式下將會被忽略,在嚴格模式下將會拋出錯誤。
  • 只指定setter函數意味着屬性不能讀。嘗試讀取屬性,在非嚴格模式下將會返回undefined,在嚴格模式下將會拋出錯誤。
var book = {
    _year: 2004,
    edition: 1
};

Object.defineProperty(book, 'year', {
    get: function() {
        return this._year;
    },
    set: function(newYear) {
        if (newYear > 2004) {
            this._year = newYear;
            this.edition += (newYear - 2004);
        }
    }
});

// 測試
book.year = 2005;  // 注意:defineProperty中定義的屬性名爲year,而非_year
console.log(book.edition);  // 2

另外一種設置getter和setter函數的方法以下:對象

var myObject = {
    // 定義getter
    get foo() {
        return 'You get: ' + this._foo_;
    },
    // 定義setter
    set foo(val) {
        this._foo_ = val;
    }
}

// 測試
myObject.foo = 2;
console.log(myObject.foo);  // You get: 2
讀取:

一樣使用Object.getOwnPropertyDescriptor()方法取得給定屬性的描述符。ip

  • 在調用Object.defineProperty()方法時,若是不指定,configurable、enumerable的默認值爲false,get、set的默認值爲undefined。
  • 對於直接定義在對象上的屬性,configurable、enumerable的默認值爲true。
// 使用Object.definedProperty設置的屬性
var descriptor = Object.getOwnPropertyDescriptor(book, 'year');
console.log(descriptor.configurable);  // false
console.log(descriptor.enumerable);  // false
console.log(typeof descriptor.get);  // function
console.log(typeof descriptor.set);  // function

// 直接定義在對象上的屬性
var descriptor = Object.getOwnPropertyDescriptor(myObject, 'foo');
console.log(descriptor.configurable);  // true
console.log(descriptor.enumerable);  // true
console.log(typeof descriptor.get);  // function
console.log(typeof descriptor.set);  // function

參考:《JavaScript高級程序設計》、《你不知道的JavaScript(上卷)》

相關文章
相關標籤/搜索