一. js中物件到底是什麼?
首先,大家想象我們平時都使用了物件的哪些操作?
常見的可能就是建立物件,然後取值,設值,例子如下:
var obj = {
name: 'aaa' //定義屬性
}
obj.name = 'bbb'; //設定屬性值
console.log(obj.name); //讀取屬性值
複製程式碼
那到底如何取值,設值,內部是如何實現的呢?這就是我們本節要詳解的內容。
接下來先說說:屬性特性
二: 屬性特性是什麼?
我們平時的說法是:建立一個物件,然後給物件設定多個屬性和屬性值(即key:value),那給物件設定的這些屬性又有哪些特性呢?
- configurable: 是否可以delete刪除屬性,是否可以修改屬性特性
- enumerable: 是否可通過for...in..迴圈訪問屬性
- writerable: 是否可以修改屬性的值
- 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"}
複製程式碼
- 我們來看一下name1:
//這裡只是設定了value預設值,其他三個屬性都沒設定,則其他三個屬性特性為false。
console.log(Object.getOwnPropertyDescriptor(obj, 'name1'));
//{value: "aaa", writable: false, enumerable: false, configurable: false}
說明:我們使用defineProperties或者defineProperty定義屬性的特性時,如果沒寫某屬性特性,則定義後結果為false.
複製程式碼
- 然後是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
複製程式碼
- 然後是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。
}
複製程式碼
- 然後是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的預設實現。
複製程式碼