深入理解物件的資料屬性與訪問器屬性

disable發表於2021-09-09

建立物件的方式有兩種:第一種,透過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);
    }
}

圖片描述

這兩種方式建立出來的物件是一樣的,有相同的屬性和方法。這些屬性內部都有描述其行為的屬性描述符。

Object.defineProperty() 

透過Object.defineProperty() 可以直接在物件上建立一個屬性,也可以修改已有的屬性。

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

  obj:屬性所在的物件  

  prop:要訪問的屬性名

  descriptor:描述符物件

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

透過以上兩種方式新增的物件屬性,其布林值特性預設值是true,透過Object.defineProperty來修改屬性特性時,只設定需要修改的特性即可;而透過Object.defineProperty建立的屬性,其布林值特性預設值是false。

ECMAScript中屬性分為兩種:資料屬性和訪問器屬性。

資料屬性

資料屬性包含四個屬性描述符:

[[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時,

  1、不可以透過delete去刪除該屬性從而重新定義屬性;

  2、不可以轉化為訪問器屬性;

  3、configurable和enumerable不可被修改;

  4、writable可單向修改為false,但不可以由false改為true;

  5、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 falseperson.d = 4; // 如果使用直接賦值的方式建立物件的屬性,則這個屬性的enumerable預設為truefor (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;
    }
  }
});

圖片描述

原文出處:https://www.cnblogs.com/shiningly/p/9482283.html

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/200/viewspace-2812294/,如需轉載,請註明出處,否則將追究法律責任。

相關文章