由可枚舉屬性引起的
JavaScript屬性分為可枚舉屬性和不可枚舉屬性常拓?什么是“枚舉”,它又有什么用呢?
var apple = {
name : "蘋果",
price :18
}
for(var prop in obj){
console.log(prop)
}
// 輸出:name ,price
// 這里的可枚舉性就是說for的這種寫法可以得到這個(gè)對(duì)象的屬性名
var stu = {};
Object.defineProperties(stu, {
name: {
value: "張三",
enumerable: false
},
age: {
value: 18,
enumerable: true
}
});
for(var prop in stu){
console.log(prop);
}
// 輸出: age
//name屬性不能便利出來厂汗,因?yàn)樗?“enumerable” 值為false
//以上代碼請(qǐng)?jiān)赾hrome或者火狐里面運(yùn)行,IE9以下運(yùn)行第二段代碼會(huì)出錯(cuò)
我們知道呜师,for...in
語句以任意順序遍歷一個(gè)對(duì)象的可枚舉屬性娶桦。
既 stu.age 可枚舉,stu.name 不可枚舉,而他們是否可枚舉是通過 enumerable
來設(shè)置的衷畦。
屬性描述符
我們通常會(huì)直接創(chuàng)建對(duì)象栗涂,然后設(shè)置對(duì)象的屬性,例如上面的 obj 對(duì)象霎匈,設(shè)置了 name ,price 屬性戴差。其實(shí)這些屬性也有一定的限定的,這些限定屬性性質(zhì)的我們稱為“屬性描述符”铛嘱。
這里拿 price 屬性來說暖释,我們能輸出 fruit.price 等于 18 ,其實(shí)是通過 price 的描述符 value
來設(shè)置的墨吓。例如上面的stu.age 的設(shè)置球匕;
price 也有“enumerable”描述符,他之所以能被 for in 到 ,是因?yàn)閷?duì)自身添加屬性的 “enumerable” 默認(rèn)為 true帖烘。
除了上面說到過的 “value” 和 “enumerable” 還有什么描述符呢亮曹?
每個(gè)屬性都有
Configurable描述符、Enumerable描述符秘症、Writable描述符照卦、Value描述符
或者,每個(gè)屬性都有
Configurable描述符乡摹、Enumerable描述符役耕、Get描述符、Set描述符
前者稱為 數(shù)據(jù)描述符聪廉,他們的值決定了該js對(duì)象的屬性的某些性質(zhì)及是否可讀寫瞬痘。
后者稱為 讀取描述符,他們的值決定了該js對(duì)象的屬性的某些性質(zhì)及讀寫的行為板熊。
描述符必須是這兩種形式之一框全,不能同時(shí)是兩者。
那么這些屬性是各代表著什么干签?
數(shù)據(jù)描述符具有以下鍵值
鍵 | 值(默認(rèn)值) | 作用 |
---|---|---|
configurable | false --------------------- | 為 true 時(shí)津辩,該屬性描述符才能夠被改變,表示對(duì)象的屬性是否可以被刪除筒严,以及除writable特性外的其他特性是否可以被修改丹泉。 |
enumerable | false | true時(shí),該屬性在對(duì)象中才是可枚舉的 |
value | undefined | 該屬性對(duì)應(yīng)的值鸭蛙∧『蓿可以是任何有效的 JavaScript 值(數(shù)值,對(duì)象娶视,函數(shù)等)晒哄。讀取屬性的時(shí)候就是通過這里開始讀 |
writable | false | 表示能否修改屬性的值賦值運(yùn)算符(assignment operator)基于右值(right operand)的值睁宰,給左值(left operand)賦值。")改變 |
讀取描述符具有以下鍵值
鍵 | 值(默認(rèn)值) | 作用 |
---|---|---|
configurable | false --------------------- | 為 true 時(shí)寝凌,該屬性描述符才能夠被改變 |
enumerable | false | true時(shí)柒傻,該屬性在對(duì)象中才是可枚舉的 |
get | undefined | 在讀取屬性時(shí)調(diào)用的函數(shù) |
set | undefined | 在設(shè)置屬性時(shí)調(diào)用的函數(shù) |
知道這些描述符所控制的性質(zhì),那又是什么時(shí)候去哪里設(shè)置的呢较木?這就關(guān)乎到Object.defineProperty() 了红符,它有三個(gè)參數(shù),Object.defineProperty(obj, prop, descriptor)伐债,其中descriptor就是設(shè)置屬性性質(zhì)描述符预侯。
defineProperty 和 defineProperties
- 語法
Object.defineProperty(obj, prop, descriptor)
Object.defineProperties(obj, props) - 定義:
Object.defineProperty() 直接在一個(gè)對(duì)象上定義一個(gè)新屬性,或者修改現(xiàn)有屬性峰锁,并返回該對(duì)象萎馅。
Object.definePropertys() 直接在一個(gè)對(duì)象上定義一個(gè)或多個(gè)新的屬性,或者修改現(xiàn)有屬性虹蒋,并返回該對(duì)象糜芳。 - 參數(shù)
obj 要在其上定義屬性的對(duì)象。
prop 要定義或修改的屬性的名稱魄衅。
descriptor 將被定義或修改的屬性描述符峭竣。 - 返回值
返回被操作的對(duì)象,即返回obj參數(shù) - 注意點(diǎn)
1)當(dāng)把configurable值設(shè)置為false后,就不能修改任何屬性了,包括自己本身這個(gè)屬性
2)想用訪問器屬性模擬默認(rèn)行為的話,必須得在里面新頂一個(gè)屬性,不然的話會(huì)造成循環(huán)引用
3)可枚舉屬性 對(duì)for/in
,Object.keys()
,JSON.stringify()
,Object.assign()
方法才生效(for/in 是對(duì)所有可枚舉屬性晃虫,而其他三種是對(duì)自身可枚舉屬性) - 用途
1)vue通過getter-setter函數(shù)來實(shí)現(xiàn)雙向綁定
2)俗稱屬性掛載器
3)專門監(jiān)聽對(duì)象數(shù)組變化的Object.observe()(es7)也用到了該方法
知道了Object.defineProperty()這個(gè)東東是用來生成或修改一個(gè)對(duì)象屬性邪驮,知道對(duì)象屬性的性質(zhì)是靠descriptor這個(gè)參數(shù)來設(shè)置之后,我們來看看他是怎么運(yùn)用的傲茄。
以var person = {}
為例,我們要怎樣去修改默認(rèn)的屬性值呢沮榜?
- 設(shè)置該屬性為數(shù)據(jù)描述符
var person = {}
Object.defineProperty(person,'a',{
configurable:true, //可修改默認(rèn)屬性
enumerable:true, //可枚舉
writable:true, //可修改這個(gè)屬性的值
value:1 //定義一個(gè)初始的值為1
})
console.log(person) //{a: 1}
person.a=2
console.log(person) //{a: 2}
for(var k in person){
console.log(k) //a
}
現(xiàn)在我們來修改下enumerable和writable值
Object.defineProperty(person,'a',{
configurable:true,
enumerable:false,
writable:false,
value:1
})
console.log(person) //{a: 1}
person.a=2
console.log(person) //{a: 1} 因?yàn)閣ritable值被設(shè)置為false了,所以不可以寫,嚴(yán)格模式下會(huì)報(bào)錯(cuò)
for(var k in person){
console.log(k)// 沒有可枚舉屬性,因?yàn)閍的enumerable的值被設(shè)置為false
}
我們?cè)囋嚢蒫onfigurable的值改為false
Object.defineProperty(person,'a',{
configurable:false,//為false的時(shí)候不允許修改默認(rèn)屬性了
})
===============================
# 改為false之后再試試修改其他屬性
Object.defineProperty(person,'a',{
configurable:true,
enumerable:true,
writable:true,
value:1
})
//woa,控制臺(tái)直接報(bào)錯(cuò)了!連想把false值改回true都不行!也就是說,這個(gè)改動(dòng)是一次性了!
//也就是說,你可以使用Object.defineProperty()方法無限修改同一個(gè)屬性,但是當(dāng)把configurable改為false之后就什么都不能再修改了
- 設(shè)置該屬性為讀取描述符
var person = {
a:1
}
Object.defineProperty(person,'a',{
get(){
return 3 //當(dāng)訪問這個(gè)屬性的時(shí)候返回3
},
set(val){
console.log(val)//當(dāng)設(shè)置這個(gè)屬性的時(shí)候執(zhí)行,val是設(shè)置的值
}
})
person.a// 3,我們明明寫的是a:1,怎么返回的3呢?這就是get()的威力了
person.a = 5// 5,相應(yīng)的設(shè)置的時(shí)候執(zhí)行了set()函數(shù)
我們來模擬一個(gè)訪問和設(shè)置的默認(rèn)行為
var person = {
a:1
}
// 注:里面的this指向ogj(person)
Object.defineProperty(person,'a',{
get(){
return this.a
},
set(val){
this.a = val
}
})
//我們想當(dāng)然的這么寫.
person.a//Uncaught RangeError: Maximum call stack size exceeded
什么,溢出了?這是為什么?
哦~原來是這么寫的話會(huì)造成循環(huán)引用,狂call不止
我們看下流程:
person.a → get.call(person) → this.a → person.a → get.call(person) → this.a......
我們來修改下
var person = {
a:1
}
Object.defineProperty(person,'a',{
get(){
return this._a || 1 //定義一個(gè)新的屬性和一個(gè)默認(rèn)值
},
set(val){
this._a = val
}
})
person.a// 1
person.a=2// 2
person.a// 2
這樣就好了
這就是數(shù)據(jù)描述符和讀取描述符的應(yīng)用方式盘榨。在平時(shí)簡單的開發(fā)中可能用不上,但是知道了這些之后對(duì)一些框架的封裝的理解還是很有幫助的蟆融,例如vue數(shù)據(jù)雙向綁定原理上利用的就是Object.defineProperty方法草巡。
拓展
每個(gè)對(duì)象都有的一些方法:
Object.getOwnPropertyDescriptors(obj)
定義:獲取一個(gè)對(duì)象的所有自身屬性(自身屬性)的描述符
使用:Object.getOwnPropertyDescriptors(apple)Object.getOwnPropertyDescriptor(obj, prop)
定義:返回指定對(duì)象上一個(gè)自有屬性對(duì)應(yīng)的屬性描述符
使用:Object.getOwnPropertyDescriptor(apple, 'price')
Object.getOwnPropertyDescriptor 的應(yīng)用:
一般直接添加屬性時(shí),屬性描述符默認(rèn)值都為 true型酥,當(dāng)用 Object.defineProperty() 方法來添加對(duì)象屬性時(shí)山憨,此時(shí)的屬性描述符默認(rèn)值為false
以文章開頭的 apple 和 stu 的案例:
// 直接添加屬性
Object.getOwnPropertyDescriptor(apple, 'name')
//name:{value: "蘋果", writable: true, enumerable: true, configurable: true}
// 屬性描述符添加屬性
Object.getOwnPropertyDescriptor(stu, 'name')
//name:{value: "張三", writable: false, enumerable: false, configurable: false}
// 又或者
var stu2 = Object.create({}, { name: { value: '李四' } })
Object.getOwnPropertyDescriptor(stu2, 'name')
//name:{value: "李四", writable: false, enumerable: false, configurable: false}
Object.create()
的第二個(gè)參數(shù)為 Object.defineProperties
的第二個(gè)參數(shù),既設(shè)置屬性及屬性描述符弥喉, 詳情移步到 Object.create()
參考資料:
1.MDN
3.關(guān)于 Object.defineProperty() 小結(jié)