前言
JavaScript 中允許使用一些內部特性來描述屬性的特徵,本文來總結一下物件內部屬性與 Object.defineProperty() 的相關知識。
正文
1、屬性型別
js中使用某些內部屬性來描述屬性的特徵,比如描述屬性是否可以列舉,是否可以修改等特徵,我們無法訪直接問屬性的這些特徵,但是可以通過[[]]的方式來將某個特性標識為內部屬性。這些內部屬性分為資料屬性和訪問器屬性。
(1)資料屬性
資料屬性包含一個儲存資料值的位置。值會從這個位置讀取,也會寫入到這個位置。資料屬性有 4個特性描述它們的行為。
a、[[Configurable]]表示能否通過 delete 刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把資料屬性修改為訪問器屬性,預設 true。
b、[[Enumerable]]表示能否通過 for-in 迴圈遍歷出屬性 預設 true。
c、[[Writable]]表示能否修改屬性的值,預設 true,設定為 false 指改屬性只讀。
d、[[Value]]儲存這個屬性的資料值,讀寫屬性的值都是在這個位置,預設值為 undefined。
要修改物件預設資料屬性,必須使用es5的 Object.defineProperty(),該方法包含三個引數,給其新增屬性的物件、屬性的名稱和一個描述符物件。如下:
var person = {}; Object.defineProperty(person, "age", { enumerable: true, configurable: false, value: 18 }); console.log(person.age);// 18 delete person.age console.log(person.age);// 18 Object.defineProperty(person, "height", { enumerable: false, value: 50 }); console.log(person);//{age:18,height:50} for (const key in person) { console.log(key, person[key]);// age 18 } Object.defineProperty(person, "name", { writable: false, value: "Nicholas" }); console.log(person.name); // "Nicholas" person.name = "Greg"; console.log(person.name); // "Nicholas"
注意:在呼叫 Object.defineProperty() 時, configurable 、 enumerable 和 writable 的值如果不指定,則都預設為 false 。
(2)訪問器屬性
訪問器屬性 ==訪問器屬性不包含資料值,他們包含setter和getter函式,這兩個函式並不是必須的
a、[[Configurable]]表示能否通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把資料屬性修改為訪問器屬性,預設 true。
b、[[Enumerable]]表示能否通過for-in迴圈遍歷出屬性 預設 true。
c、[[Set]]寫入屬性的函式,預設 undefined。
d、[[Get]]讀取屬性的函式,預設 undefined。
同樣,訪問器屬性是不能直接定義的,必須使用 Object.defineProperty() 。如下:
var person = { name: "Nicholas", isAdult: true }; Object.defineProperty(person, 'age', { get: function () { return this.age }, set: function (newValue) { this.isAdult = newValue >= 18 ? true : false } }); console.log(person.isAdult); person.age = 17 console.log(person.isAdult);
注意:設定了訪問器屬性之後不能再設定資料屬性,比如設定了 set/get 就不能再設定 writable。
2、補充:內部屬性的操作
(1)一次性定義多個屬性
var person = {} Object.defineProperties(person, { 'name': { value: "Nicholas" }, 'isAdult': { writable: true, configurable: true, value: true }, 'age': { configurable: true, get() { return this.age }, set(newValue) { this.isAdult = newValue >= 18 ? true : false } } }) console.log(person.isAdult);// true person.age = 17// 首先到age的set方法,然後訪問isAdult的writable屬性並修改value值 console.log(person.isAdult);// false
(2)讀取屬性的特性 Object.getOwnPropertyDescriptor()獲取傳入物件給定屬性的描述符。Object.getOwnPropertyDescriptors()獲取給定物件的全部屬性描述符。
var person = { name: "Nicholas", isAdult: true }; Object.defineProperty(person, 'age', { get: function () { return this.age }, set: function (newValue) { this.isAdult = newValue >= 18 ? true : false } }); var descriptor = Object.getOwnPropertyDescriptor(person, 'age') console.log(descriptor); //configurable: false // enumerable: false // get: ƒ () // set: ƒ (newValue) console.log(Object.getOwnPropertyDescriptors(person)); // {name:{...},isAdult:{...},age:{...}}
3、物件內部特性和 Object.defineProperty() 應用
(1)手寫實現 const
function myConst(key, value) { window.key = value // 把要定義的key掛載到window下,並賦值value Object.defineProperty(window, key, { enumerable: false, configurable: false, get: function () { return value }, set: function (data) { if (data !== value) { // 當要對當前屬性進行賦值時,則丟擲錯誤! throw new TypeError('Assignment to constant variable.') } else { return value } } }) } myConst('a', 2) console.log(a); a = 3//報錯 :Assignment to constant variable.
(2)手動實現每次訪問一個屬性時,值加一,使得a==1&&a==2&a==3成為可能
window.b = 0 Object.defineProperty(window, "a", { get() { this.b++ return this.b }, set(value) { this.b = value } }) if (a == 1 && a == 2 & a == 3) { console.log("111"); }//111
寫在最後
以上就是本文的全部內容,希望給讀者帶來些許的幫助和進步,方便的話點個關注,小白的成長之路會持續更新一些工作中常見的問題和技術點。