js?通過Object.defineProperty()?定義和控制對象屬性
Object.defineProperty()
Object.defineProperty() 用于給一個對象定義一個新屬性或是修改某個現(xiàn)有屬性,并返回此對象,事實上就算不定義變量去接收返回值,該對象也會被直接修改(所以它不是一個純函數(shù))。它接收 3 個參數(shù),第 1 個是要定義屬性的對象;第 2 個是要定義或修改的屬性的屬性名或 Symbol;第 3 個是對該屬性的描述,稱之為屬性描述符,為一個對象,可以擁有 4 個 key。
屬性描述符
屬性描述符可以分為 2 類——數(shù)據(jù)屬性描述符和存取屬性描述符,它們有 2 個共同的可擁有的 key: configurable 和 enumerable。區(qū)別在于剩下 2 個,數(shù)據(jù)屬性描述符為 value 和 writable 而存儲屬性描述符為 get 和 set。
下圖截取自 MDN 文檔:

一個屬性如果擁有了 value 或 writable,那么它就是數(shù)據(jù)描述符,它就不能同時擁有 get 或 set;反之,如果擁有了 get 或 set,那么它就是存取描述符,同理不能再擁有 value 或 writable。 接下來詳細介紹下上圖中的 6 個特性:
configurable
描述該屬性是否可以被刪除:
'use strict'
const obj = { singer: 'Jay' }
Object.defineProperty(obj, 'singer', {
configurable: false
})
delete obj.singer我們將 obj 的 singer 屬性的 configurable 設(shè)置為了 false,所以在第 6 行使用 delete 刪除 singer 屬性時,在嚴格模式下(在非嚴格模式下不會報錯,但也不會刪除 singer,即靜默失?。?,瀏覽器會報錯:

2. 描述該屬性是否可以再次通過 Object.defineProperty() 來修改屬性描述:
const obj = { singer: 'Jay' }
Object.defineProperty(obj, 'age', {
configurable: false
})
Object.defineProperty(obj, 'age', {
enumerable: flase
})當(dāng)我們將 age 的 configurable 設(shè)為 false 后,在第 6 行試圖修改 age 的 enumerable 特性,會報錯“TypeError: Cannot redefine property: age”。當(dāng)設(shè)置 configurable 為 false 后(writable 默認為 false),該屬性的任意描述符(enumerable,value 或是 get、set)都不能被改變了。但是,如果初始定義時 writable 為 true,即使 configurable 為 false,那么接下去還是可以將 writable 改為 false,同時也可以修改 value:
const obj = new Object()
obj.singer = 'Jay'
Object.defineProperty(obj, 'age', {
configurable: false,
writable: true
})
console.log(obj.age) // undefined
Object.defineProperty(obj, 'age', {
writable: false,
value: 40
})
console.log(obj.age) // 40注:上面采用了 new Object() 的方式定義了一個對象,和直接通過字面量定義對象作用一樣。
描述該屬性是否可以修改屬性描述符類型:
const obj = { singer: 'Jay' }
Object.defineProperty(obj, 'singer', {
configurable: false
})
Object.defineProperty(obj, 'singer', {
get() {
return 'Zhou'
}
})上面這個例子里,在第 1 行我們通過字面量的方式直接定義了 obj 對象,其屬性 singer 的描述符默認為數(shù)據(jù)描述符,在第 3 行我們將其的 configurable 設(shè)為 false,然后在第 6 行給它定義一個 getter 函數(shù),也就是試圖將它改為存取描述符,瀏覽器同樣會報 "Cannot redefine property: singer" 錯誤。 如果我們新定義一個 age 屬性,讓其的屬性描述符為存取描述符,但是 configurable 依舊設(shè)置為 false:
const obj = { singer: 'Jay', _age: 40 }
Object.defineProperty(obj, 'age', {
configurable: false,
enumerable: true,
get() {
return this._age
},
set(val) {
this._age = val
}
})
Object.defineProperty(obj, 'age', {
value: 35
})那么我們想再次給 age 的屬性描述符一個 value 特性,想將之改為數(shù)據(jù)描述符,瀏覽器也會報錯。
enumerable
描述該屬性是否是可枚舉的。比如現(xiàn)在有如下代碼,我們通過 Object.defineProperty() 給 obj 定義 age 屬性, 并設(shè)置 enumerable 為 false:
var obj = { singer: 'Jay' }
Object.defineProperty(obj, 'age', {
enumerable: false
})現(xiàn)在,當(dāng)我使用 for in 遍歷 obj 時,只能得到 singer 而得不到 age,因為 for in 是以任意順序遍歷 obj 的除 Symbol 以外的可枚舉屬性(包括原型上的屬性):
for (const key in obj) {
console.log(key) // singer
}使用 Object.keys() 遍歷得到的數(shù)組也只包含 singer,因為 Object.keys() 返回的是 obj 自身的可枚舉屬性組成的數(shù)組:
console.log(Object.keys(obj)) // ['singer'] 復(fù)制代碼
如果是直接打印 obj 對象,那么在 Node.js 中運行將看不到 age,在 Chrome 瀏覽器中可以看到,但是 age 是淺色的:

如果想查看一個 enumerable 為 false 的屬性(比如 obj 的 age 屬性),除了可以直接通過 obj.age 查看,還可以通過 Object.getOwnPropertyNames()——返回自身除 Symbol 值作為名稱的屬性之外的所有屬性,或是 Reflect.ownKeys()——獲取自身所有的屬性:
console.log(Object.getOwnPropertyNames(obj)) // [ 'singer', 'age' ] console.log(Reflect.ownKeys(obj)) // [ 'singer', 'age' ]
writable
數(shù)據(jù)描述符專有特性,描述屬性是否可修改。
'use strict'
var obj = { singer: 'Jay' }
Object.defineProperty(obj, 'singer', {
writable: false
})
obj.singer = 10上例中,在嚴格模式下,在第 6 行給 writable 為 false 的屬性 singer 賦值,會報錯 “TypeError: Cannot assign to read only property 'age' of object '#'”。在非嚴格模式下不會報錯,但也不會修改屬性 singer 的值。 另外,通過前面的例子可以看出,writable 的優(yōu)先級是高于 configurable 的。
value
數(shù)據(jù)描述符專有特性,為屬性的值。讀取屬性時返回該值;修改屬性時則修改該值。
get
當(dāng)屬性被獲取時,會執(zhí)行 getter 函數(shù)。
set
當(dāng)屬性被設(shè)置時,會執(zhí)行 setter 函數(shù)。
const obj = { singer: 'Jay', _age: 40 }
Object.defineProperty(obj, 'age', {
get() {
return this._age
},
set(value) {
this._age = value
}
})
console.log(obj.age) // 40
obj.age = 50
console.log(obj.age) // 50用存取描述符定義的屬性,直接打印查看對象時,比如我們將上例中 obj 的 age 屬性的 enumerable 設(shè)為 true,然后 console.log(obj),會發(fā)現(xiàn)其結(jié)果為“{ singer: 'Jay', _age: 40, age: [Getter/Setter] }” 。 順便說一句,vue2 的響應(yīng)式原理就用到了 getter 和 setter,具體可前往《vue.js數(shù)據(jù)響應(yīng)式原理解析》。
默認值
直接給對象定義屬性時:
- configurable 默認為 true;
- enumerable 默認為 true;
- writable默認為 true;
- value 默認為定義時賦的值;
- get 默認為 undefined;
- set 默認為 undefined;
通過屬性描述符定義一個屬性時:
- configurable 默認為 false;
- enumerable 默認為 false;
- writable默認為 false;
- value 默認為 undefined;
- get 默認為 undefined;
- set 默認為 undefined;
獲取屬性的描述符
如果想要驗證上面的結(jié)論,我們可以通過 Object.getOwnPropertyDescriptor(對象, '屬性名') 查看某個對象自有屬性的屬性描述符,或是 Object.getOwnPropertyDescriptors(對象) 查看某個對象的所有的自身屬性的屬性描述符。
Object.defineProperties()
Object.defineProperty() 是一次定義 / 修改一個屬性,傳入 3 個參數(shù)。如果我們想一次性通過屬性描述符定義 / 修改多個屬性,可以使用 Object.defineProperties(),它傳入 2 個參數(shù),第 1 個參數(shù)為要定義屬性的對象;第二個參數(shù)為一個對象,其鍵名為要定義的屬性,鍵值為一個對象,里面就是關(guān)于該屬性的屬性描述符定義。
比如:
const obj = { singer: 'Jay', _age: 40 }
Object.defineProperties(obj, {
age: {
configurable: true,
enumerable: true,
get() {
return this._age
},
set(val) {
this._age = val
}
},
gender: {
configurable: true,
enumerable: true,
writable: true,
value: '男'
}
})對象本身的兩個方法
其實每個對象本身都可以直接使用 getter —— 得到當(dāng)前屬性值的回調(diào)函數(shù),和 setter —— 監(jiān)視當(dāng)前屬性值變化的回調(diào)函數(shù),來定義屬性,比如:
const obj = {
firstName: 'Jay',
lastName: 'Zhou',
get fullName() {
return this.firstName + ' ' + this.lastName
},
set fullName(val) {
const tempArr = val.split(' ')
this.firstName = tempArr[0]
this.lastName = tempArr[1]
}
}到此這篇關(guān)于js 通過Object.defineProperty() 定義和控制對象屬性的文章就介紹到這了,更多相關(guān)js Object.defineProperty內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript判斷變量是否為數(shù)組的方法(Array)
這篇文章主要介紹了JavaScript判斷變量是否為數(shù)組的方法(Array),涉及到j(luò)avascript 數(shù)組 變量相關(guān)知識,感興趣的朋友一起學(xué)習(xí)吧2016-02-02
js匿名函數(shù)的調(diào)用示例(形式多種多樣)
匿名函數(shù)就是沒有實際名字的函數(shù),javaScript的匿名函數(shù)形式多樣,下面就一一為大家羅列出來2014-08-08

