深刻理解對象的數據屬性與訪問器屬性

建立對象的方式有兩種:第一種,經過new操做符後面跟Object構造函數,第二種,對象字面量方式。以下函數

var person = new Object(); person.name = 'Nicy'; person.age = 21; person.sayName = function() { console.log(this.name); }; var person = { name: 'Nicy', age: 21, sayName: function() { console.log(this.name); } }

這兩種方式建立出來的對象是同樣的,有相同的屬性和方法。這些屬性內部都有描述其行爲的屬性描述符。this

Object.defineProperty() 

經過Object.defineProperty() 能夠直接在對象上建立一個屬性,也能夠修改已有的屬性。spa

Object.defineProperty(obj, prop, descriptor) 接收三個參數:code

  obj:屬性所在的對象  對象

  prop:要訪問的屬性名blog

  descriptor:描述符對象遞歸

描述符對象包含六個屬性:configurable、enumerable、writable、value、get、set ,要修改屬性的特性,必須使用Object.defineProperty()方法。ip

經過以上兩種方式添加的對象屬性,其布爾值特性默認值是true,經過Object.defineProperty來修改屬性特性時,只設置須要修改的特性便可;而經過Object.defineProperty建立的屬性,其布爾值特性默認值是false。內存

ECMAScript中屬性分爲兩種:數據屬性和訪問器屬性。get

數據屬性

數據屬性包含四個屬性描述符:

[[Configurable]] : 表示可否經過delete刪除屬性從而從新定義屬性,可否修改屬性特性,可否把屬性修改成訪問器屬性。經過以上方式添加的對象屬性,默認爲true。

[[Enumerable]] : 表示可否經過for-in 循環訪問屬性。經過以上方式添加的對象屬性,默認爲true。

[[Writable]] : 表示可否修改屬性的值。經過以上方式添加的對象屬性,默認爲true。

[[Value]] : 包含這個屬性的數據值,可讀取寫入。經過以上方式添加的對象屬性,默認爲undefined。

Writable

var person = {}; Object.defineProperty(person, "name", { value: 'Nicy' }) person.name = 'Lee';   console.log(person.name) // 'Nicy'
 Object.defineProperty(person, "name", { writable: true }) person.name = 'Lee'; console.log(person.name) // 'Lee'

Object.defineProperty直接建立的屬性writable默認爲false,value值不可修改,此時修改name爲Lee,在非嚴格模式下不會報錯,但操做被忽略,在嚴格模式下會報錯。

Configurable

var person = { name: 'Nicy', age: 21, sayName: function() { console.log(this.name); } } Object.defineProperty(person, "name", { configurable: false }) delete person.name;    // 操做被忽略,沒法經過delete刪除屬性
Object.defineProperty(person, "name", { // throw error configurable:true })
Object.defineProperty(person,
"name", { // throw error enumerable: false })
Object.defineProperty(person,
"name", { // 因爲writable爲true,因此能夠修改value value: 'Lucy' })
console.log(person.name)
// Lucy

Object.defineProperty(person, "name", { // writable可進行true -> false的單向修改 writable: false })
Object.defineProperty(person,
"name", { // throw error value: 'Lee' })
Object.defineProperty(person,
"name", { // throw error,此時writable不能夠false -> true writable: true })

總結一下configurable:當configurable設爲false時,

  一、不能夠經過delete去刪除該屬性從而從新定義屬性;

  二、不能夠轉化爲訪問器屬性;

  三、configurable和enumerable不可被修改;

  四、writable可單向修改成false,但不能夠由false改成true

  五、value是否可修改根據writable而定。

當configurable爲false時,用delete刪除該屬性,在非嚴格模式下,不會報錯,但操做被忽略,在嚴格模式下會報錯;其餘不可被修改的特性修改時會報錯。

Enumerable

enumerable表示對象屬性是否能夠在for...in和Object.keys()中被枚舉。

var person = {}; Object.defineProperty(person, "a", { value : 1, enumerable:true }); Object.defineProperty(person, "b", { value : 2, enumerable:false }); Object.defineProperty(person, "c", { value : 3 }); // enumerable defaults to false
person.d = 4; // 若是使用直接賦值的方式建立對象的屬性,則這個屬性的enumerable默認爲true

for (var i in person) { console.log(i); }  // 'a' 和 'd' 
 Object.keys(person); // ["a", "d"]

訪問器屬性

訪問器屬性包含四個屬性描述符:

[[Configurable]] : 表示可否經過delete刪除屬性從而從新定義屬性,可否修改屬性特性,可否把屬性修改成數據屬性。直接在對象上定義的屬性,默認爲true。

[[Enumerable]] : 表示可否經過for-in 循環訪問屬性。直接在對象上定義的屬性,默認爲true。

[[Get]] : 讀取屬性時調用的函數,默認爲undefined。

[[Set]] : 寫入屬性時調用的函數,默認爲undefined。

var person = {
    name: 'Nicy',
    _age: 21,
    year: 1997,
    _year: 1997,
    sayName: function() {
        console.log(this.name);
    }
}

Object.defineProperty(person, "age", {
    get: function() {
        return this._age;
    },
    set: function(value) {
        this._age = value;
                // ...
    }
})

用Object.defineProperty()定義的訪問器屬性,其configurable和enumerable默認爲false。

數據屬性與訪問器屬性的相互轉換

Object.getOwnPropertyDescriptor 讀取屬性的特性

使用Object.getOwnPropertyDescriptor能夠獲取到屬性的描述符:

Object.getOwnPropertyDescriptor(obj, prop)

  obj:屬性所在的對象;

  prop:要訪問的屬性名。

數據屬性 -> 訪問器屬性

屬性的特性只能是訪問器描述符和數據描述符中的一種,給已有的數據屬性加get或set轉換爲訪問器屬性時,其屬性的value、writable就會被廢棄。

以下代碼,將對象原有的數據屬性year轉換爲訪問器屬性:

*注:在訪問器屬性的get和set中,不可使用this訪問屬性自己,不然會無限遞歸而致使內存泄漏。

// 設置get和set其中任意一個便可轉換爲訪問器屬性
Object.defineProperty(person, "year", {
    get: function() {
//        return this,year;    // error
        return this._year;    
    },
    set: function(value) {
//             this.year = value;  // error
        this._year= value;
    }
})

var descriptor = Object.getOwnPropertyDescriptor(person, 'year');
console.log(descriptor);    // {get: ƒ, set: ƒ, enumerable: true, configurable: true}

在原有的數據屬性year中,使用Object.defineProperty()爲屬性設置get 或 set,均可以將其轉換爲訪問器屬性。

訪問器屬性 -> 數據屬性

將訪問器屬性轉換爲數據屬性,只須要給現有訪問器屬性設置value或writable這兩個屬性描述符中的任意一個便可,其原有的get和set就會被廢棄,從而轉換爲數據屬性。

上面爲person定義的訪問器屬性age,經過Object.defineProperty()只設置了get和set,因此configurable默認爲false,不能夠將其轉換爲數據屬性。能夠在訪問器屬性和數據屬性間相互轉化的屬性其configurable特性值必須爲true。

以下代碼,咱們爲person新定義一個訪問器屬性job,將其configurable設置爲true ,並將其轉換爲數據屬性:

Object.defineProperty(person, "job", {
    configurable: true,
    enumerable: true,
    get: function() {
        return this._job;
    },
    set: function(value) {
        this._job = value;
    }
})

// 設置value和writable其中任意一個便可轉換爲數據屬性        
Object.defineProperty(person, "job", {
    value: 'worker',
    writable: true
})

var descriptor = Object.getOwnPropertyDescriptor(person, 'job');
console.log(descriptor);    // {value: "worker", writable: true, enumerable: true, configurable: true}

數據描述符value、writable 和訪問器描述符get、set不能同時設置,不然會報錯。

Object.defineProperties()

經過Object.defineProperties()能夠一次性爲對象定義多個屬性。

var person = {};
Object.defineProperties(person, {
  name: {
    value: 'Nicy',
    writable: true
  },
  _age: {
    value: 21,
    enumerable: true,
    writable: true,
    configurable: true
  },
   age: {
    get: function() {
    return this._age;
    },
    set: function(value) {
    this._age = value;
    }
  }
});
相關文章
相關標籤/搜索