前端進階課程之物件屬性特性詳解

沉默抒懷者發表於2018-11-15

一. js中物件到底是什麼?

首先,大家想象我們平時都使用了物件的哪些操作?

常見的可能就是建立物件,然後取值,設值,例子如下:
var obj = {
    name: 'aaa' //定義屬性
}
obj.name = 'bbb'; //設定屬性值
console.log(obj.name); //讀取屬性值
複製程式碼

那到底如何取值,設值,內部是如何實現的呢?這就是我們本節要詳解的內容。

接下來先說說:屬性特性

二: 屬性特性是什麼?

我們平時的說法是:建立一個物件,然後給物件設定多個屬性和屬性值(即key:value),那給物件設定的這些屬性又有哪些特性呢?

  1. configurable: 是否可以delete刪除屬性,是否可以修改屬性特性
  2. enumerable: 是否可通過for...in..迴圈訪問屬性
  3. writerable: 是否可以修改屬性的值
  4. value: 即屬性值就直接設定在value上

總結:前三者預設都為true,value預設為undefined

我們結合程式碼來看看:

var obj = {
    name: 'aaa'
};
console.log(Object.getOwnPropertyDescriptor(obj, 'name'));
//結果如下:
{value: "aaa", writable: true, enumerable: true, configurable: true}

說明:我們可以通過Object.getOwnPropertyDescriptor方法獲取指定物件屬性的屬性特性
複製程式碼

清楚了什麼是屬性特性以後,接下來,我們就要學會如何去定義或者修改這些屬性特性?

三. 定義或者修改屬性特性

通過上面,我們知道了物件屬性預設有什麼特性,那麼這些特性如何修改呢?以及這些特性有哪些作用?

核心方法:Ojbect.defineProperty(), 或者Object.defineProperties() 前者只能定義屬性及其特性,後者可以同時定義多個屬性及其特性

這裡我們直接採用後者定義多個屬性:
Object.defineProperties(obj, {
    name1: {
        value: 'aaa'
    },
    name2: {
        value: 'bbb',
        configurable: false
    },
    name3: {
        value: 'ccc',
        configurable: true,
        enumerable: false
    },
    name4: {
        value: 'ddd',
        configurable: true,
        enumerable: true,
        writable: false
    }
});
console.log(obj);//{name4: "ddd", name1: "aaa", name2: "bbb", name3: "ccc"}
複製程式碼
  1. 我們來看一下name1:
//這裡只是設定了value預設值,其他三個屬性都沒設定,則其他三個屬性特性為false。
console.log(Object.getOwnPropertyDescriptor(obj, 'name1'));
//{value: "aaa", writable: false, enumerable: false, configurable: false}

說明:我們使用defineProperties或者defineProperty定義屬性的特性時,如果沒寫某屬性特性,則定義後結果為false.
複製程式碼
  1. 然後是name2:
//設定configurable為false,表示無法用delete刪除,且無法再修改其屬性特性(不包括value特性,也就是說只要writable為true,則就可以修改value,configurable是控制其他三個特性是否可以修改)
console.log(Object.getOwnPropertyDescriptor(obj, 'name2'));
//{value: "bbb", writable: false, enumerable: false, configurable: false}

我們試一下:
delete obj.name2;
console.log(obj.name2);//此時無法刪除name2屬性
Object.defineProperty(obj, 'name2', {
    value: '111'
});//此時會報錯,Cannot redefine property: name2
複製程式碼
  1. 然後是name3:
//設定enumerable為false,則無法使用for..in..遍歷屬性
console.log(Object.getOwnPropertyDescriptor(obj, 'name3'));
//{value: "ccc", writable: false, enumerable: false, configurable: true}

我們試一下:
for (let key in obj) {
    console.log(key); //結果只輸出了一個name4,因為其他三個屬性的enumerable特性為false。
}
複製程式碼
  1. 然後是name4:
//設定writable為false,表示無法修改value值
console.log(Object.getOwnPropertyDescriptor(obj, 'name4'));
//{value: "ddd", writable: false, enumerable: true, configurable: true}

我們試一下:
obj.name4 = '12313';
console.log(obj.name4); // 結果依然是ddd
複製程式碼

四:自定義getter/setter特性

平時我們只是通過【物件.屬性】去訪問和設定物件屬性,其實訪問就是通過訪問器特性get去獲取的,設定就是通過訪問器特性set去設定

(說明:這裡的訪問器屬性可以理解為和屬性特性統一級別的特性,只不過作用不同,前者是定義訪問物件屬性的特性,後者是定義物件屬性本身的特性)

var obj = {
    _name: ''//說明:以_開頭定義的屬性,一般只用於物件內部方法去訪問。
};
Object.defineProperty(obj, 'name', {
    get: function () {
        return this._name
    },
    set: function (newName) {
        this._name = newName;
    }
})
obj.name = 'aaa';//其實內部間接呼叫了該屬性的set方法
console.log(obj.name);//其實內部間接呼叫屬性的get方法
//已上程式碼其他就是get/set方法預設的實現,

當然,如果我們想要去自定義get,set方法也可以,只需要把get/set的函式體改一下即可。
Object.defineProperty(obj, 'name', {
    get: function () {
        return 'aaa'
    },
    set: function (newName) {
        this._name = newName;
    }
})
obj.name = 'bbb';
console.log(obj.name);//aaa
obj.name = 'ccc';
console.log(obj.name);//aaa
此時不管怎麼樣去修改name屬性,其結果都是aaa,因為我們修改了get方法的預設實現,寫死了每次都會返回一個固定的aaa。

當然以上程式碼,只是為了說明我們平時設定獲取物件屬性的內部原理,一般不會進行修改get/set的預設實現。

複製程式碼

相關文章