js?通過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ì)該屬性的描述,稱之為屬性描述符,為一個(gè)對(duì)象,可以擁有 4 個(gè) key。
屬性描述符
屬性描述符可以分為 2 類——數(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。 接下來詳細(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. 描述該屬性是否可以再次通過 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 特性,會(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ì)象,和直接通過字面量定義對(duì)象作用一樣。
描述該屬性是否可以修改屬性描述符類型:
const obj = { singer: 'Jay' } Object.defineProperty(obj, 'singer', { configurable: false }) Object.defineProperty(obj, 'singer', { get() { return 'Zhou' } })
上面這個(gè)例子里,在第 1 行我們通過字面量的方式直接定義了 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)在有如下代碼,我們通過 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 屬性),除了可以直接通過 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
上例中,在嚴(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 的值。 另外,通過前面的例子可以看出,writable 的優(yōu)先級(jí)是高于 configurable 的。
value
數(shù)據(jù)描述符專有特性,為屬性的值。讀取屬性時(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] }” 。 順便說一句,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;
通過屬性描述符定義一個(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é)論,我們可以通過 Object.getOwnPropertyDescriptor(對(duì)象, '屬性名')
查看某個(gè)對(duì)象自有屬性的屬性描述符,或是 Object.getOwnPropertyDescriptors(對(duì)象)
查看某個(gè)對(duì)象的所有的自身屬性的屬性描述符。
Object.defineProperties()
Object.defineProperty()
是一次定義 / 修改一個(gè)屬性,傳入 3 個(gè)參數(shù)。如果我們想一次性通過屬性描述符定義 / 修改多個(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ù),來定義屬性,比如:
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() 定義和控制對(duì)象屬性的文章就介紹到這了,更多相關(guān)js Object.defineProperty內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS添加刪除一組文本框并對(duì)輸入信息加以驗(yàn)證判斷其正確性
需要添加幾組數(shù)據(jù)到數(shù)據(jù)庫,但是具體幾組數(shù)據(jù)不確定,有客戶來填寫,所以,這里我用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-04JavaScript判斷變量是否為數(shù)組的方法(Array)
這篇文章主要介紹了JavaScript判斷變量是否為數(shù)組的方法(Array),涉及到j(luò)avascript 數(shù)組 變量相關(guān)知識(shí),感興趣的朋友一起學(xué)習(xí)吧2016-02-02js匿名函數(shù)的調(diào)用示例(形式多種多樣)
匿名函數(shù)就是沒有實(shí)際名字的函數(shù),javaScript的匿名函數(shù)形式多樣,下面就一一為大家羅列出來2014-08-08JS使用Promise時(shí)常見的5個(gè)錯(cuò)誤總結(jié)
Promise?提供了一種優(yōu)雅的方法來處理?JS?中的異步操作。這也是避免“回調(diào)地獄”的解決方案。然而,并沒有多少開發(fā)人員了解其中的內(nèi)容。因此,許多人在實(shí)踐中往往會(huì)犯錯(cuò)誤。在本文中,介紹一下使用?promise?時(shí)的五個(gè)常見錯(cuò)誤,希望大家能夠避免2022-11-11