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

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

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

如果想查看一個(gè) enumerable 為 false 的屬性(比如 obj 的 age 屬性),除了可以直接通過(guò) obj.age 查看,還可以通過(guò) Object.getOwnPropertyNames()——返回自身除 Symbol 值作為名稱(chēng)的屬性之外的所有屬性,或是 Reflect.ownKeys()——獲取自身所有的屬性:
console.log(Object.getOwnPropertyNames(obj)) // [ 'singer', 'age' ] console.log(Reflect.ownKeys(obj)) // [ 'singer', 'age' ]
writable
數(shù)據(jù)描述符專(zhuān)有特性,描述屬性是否可修改。
'use strict'
var obj = { singer: 'Jay' }
Object.defineProperty(obj, 'singer', {
writable: false
})
obj.singer = 10上例中,在嚴(yán)格模式下,在第 6 行給 writable 為 false 的屬性 singer 賦值,會(huì)報(bào)錯(cuò) “TypeError: Cannot assign to read only property 'age' of object '#'”。在非嚴(yán)格模式下不會(huì)報(bào)錯(cuò),但也不會(huì)修改屬性 singer 的值。 另外,通過(guò)前面的例子可以看出,writable 的優(yōu)先級(jí)是高于 configurable 的。
value
數(shù)據(jù)描述符專(zhuān)有特性,為屬性的值。讀取屬性時(shí)返回該值;修改屬性時(shí)則修改該值。
get
當(dāng)屬性被獲取時(shí),會(huì)執(zhí)行 getter 函數(shù)。
set
當(dāng)屬性被設(shè)置時(shí),會(huì)執(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用存取描述符定義的屬性,直接打印查看對(duì)象時(shí),比如我們將上例中 obj 的 age 屬性的 enumerable 設(shè)為 true,然后 console.log(obj),會(huì)發(fā)現(xiàn)其結(jié)果為“{ singer: 'Jay', _age: 40, age: [Getter/Setter] }” 。 順便說(shuō)一句,vue2 的響應(yīng)式原理就用到了 getter 和 setter,具體可前往《vue.js數(shù)據(jù)響應(yīng)式原理解析》。
默認(rèn)值
直接給對(duì)象定義屬性時(shí):
- configurable 默認(rèn)為 true;
- enumerable 默認(rèn)為 true;
- writable默認(rèn)為 true;
- value 默認(rèn)為定義時(shí)賦的值;
- get 默認(rèn)為 undefined;
- set 默認(rèn)為 undefined;
通過(guò)屬性描述符定義一個(gè)屬性時(shí):
- configurable 默認(rèn)為 false;
- enumerable 默認(rèn)為 false;
- writable默認(rèn)為 false;
- value 默認(rèn)為 undefined;
- get 默認(rèn)為 undefined;
- set 默認(rèn)為 undefined;
獲取屬性的描述符
如果想要驗(yàn)證上面的結(jié)論,我們可以通過(guò) Object.getOwnPropertyDescriptor(對(duì)象, '屬性名') 查看某個(gè)對(duì)象自有屬性的屬性描述符,或是 Object.getOwnPropertyDescriptors(對(duì)象) 查看某個(gè)對(duì)象的所有的自身屬性的屬性描述符。
Object.defineProperties()
Object.defineProperty() 是一次定義 / 修改一個(gè)屬性,傳入 3 個(gè)參數(shù)。如果我們想一次性通過(guò)屬性描述符定義 / 修改多個(gè)屬性,可以使用 Object.defineProperties(),它傳入 2 個(gè)參數(shù),第 1 個(gè)參數(shù)為要定義屬性的對(duì)象;第二個(gè)參數(shù)為一個(gè)對(duì)象,其鍵名為要定義的屬性,鍵值為一個(gè)對(duì)象,里面就是關(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: '男'
}
})對(duì)象本身的兩個(gè)方法
其實(shí)每個(gè)對(duì)象本身都可以直接使用 getter —— 得到當(dāng)前屬性值的回調(diào)函數(shù),和 setter —— 監(jiān)視當(dāng)前屬性值變化的回調(diào)函數(shù),來(lái)定義屬性,比如:
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 通過(guò)Object.defineProperty() 定義和控制對(duì)象屬性的文章就介紹到這了,更多相關(guān)js Object.defineProperty內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS添加刪除一組文本框并對(duì)輸入信息加以驗(yàn)證判斷其正確性
需要添加幾組數(shù)據(jù)到數(shù)據(jù)庫(kù),但是具體幾組數(shù)據(jù)不確定,有客戶(hù)來(lái)填寫(xiě),所以,這里我用JS進(jìn)行添加刪除子方案,并要對(duì)方案輸入的正確性加以判斷,感興趣的朋友可以了解下2013-04-04
原生js實(shí)現(xiàn)跨瀏覽器獲取鼠標(biāo)按鍵的值
e.button W3C是獲取鼠標(biāo)按鍵 0 表示左鍵 1表示中鍵 2表示右鍵 而IE瀏覽器則是 1表示左鍵 4表示中間 2表示右鍵 這里的IE瀏覽器主要是IE8以下的瀏覽器,感興趣的朋友可以參考下哈2013-04-04
JavaScript判斷變量是否為數(shù)組的方法(Array)
這篇文章主要介紹了JavaScript判斷變量是否為數(shù)組的方法(Array),涉及到j(luò)avascript 數(shù)組 變量相關(guān)知識(shí),感興趣的朋友一起學(xué)習(xí)吧2016-02-02
js匿名函數(shù)的調(diào)用示例(形式多種多樣)
匿名函數(shù)就是沒(méi)有實(shí)際名字的函數(shù),javaScript的匿名函數(shù)形式多樣,下面就一一為大家羅列出來(lái)2014-08-08
JS使用Promise時(shí)常見(jiàn)的5個(gè)錯(cuò)誤總結(jié)
Promise?提供了一種優(yōu)雅的方法來(lái)處理?JS?中的異步操作。這也是避免“回調(diào)地獄”的解決方案。然而,并沒(méi)有多少開(kāi)發(fā)人員了解其中的內(nèi)容。因此,許多人在實(shí)踐中往往會(huì)犯錯(cuò)誤。在本文中,介紹一下使用?promise?時(shí)的五個(gè)常見(jiàn)錯(cuò)誤,希望大家能夠避免2022-11-11

