一、什么是屬性描述符?
MDN:
對(duì)象里目前存在的屬性描述符有兩種主要形式:數(shù)據(jù)描述符和存取描述符阳仔。
數(shù)據(jù)描述符是一個(gè)擁有可寫或不可寫值的屬性攒霹。
存取描述符是由一對(duì) getter-setter 函數(shù)功能來(lái)描述的屬性怯疤。
描述符必須是兩種形式之一;不能同時(shí)是兩者催束。
數(shù)據(jù)描述符和存取描述符均具有以下可選鍵值:
value 與屬性相關(guān)的值集峦。可以是任何有效的 JavaScript 值(數(shù)值抠刺,對(duì)象塔淤,函數(shù)等)。默認(rèn)為 undefined速妖。?
writable true 當(dāng)且僅當(dāng)可能用賦值運(yùn)算符改變與屬性相關(guān)的值高蜂。默認(rèn)為 false。
存取描述符同時(shí)具有以下可選鍵值:
get
一個(gè)給屬性提供 getter 的方法罕容,如果沒有 getter 則為 undefined备恤。方法將返回用作屬性的值稿饰。默認(rèn)為 undefined。 set 一個(gè)給屬性提供 setter 的方法露泊,如果沒有 setter 則為 undefined喉镰。該方法將收到作為唯一參數(shù)的新值分配給屬性。默認(rèn)為 undefined惭笑。
以上是摘自MDN的解釋梧喷,看起來(lái)是很晦澀的,具體什么意思呢: 首先我們從以上解釋知道該匿名參數(shù)對(duì)象有個(gè)很好聽的名字叫屬性描述符脖咐,屬性描述符又分成兩大塊:數(shù)據(jù)描述符以及存取描述符(其實(shí)只是一個(gè)外號(hào)铺敌,給指定的屬性集合起個(gè)外號(hào))。
數(shù)據(jù)描述符包括兩個(gè)屬性 :value屬性以及writable屬性屁擅,第一個(gè)屬性用來(lái)聲明當(dāng)前欲修飾的屬性的值偿凭,第二個(gè)屬性用來(lái)聲明當(dāng)前對(duì)象是否可寫即是否可以修改
存取描述符就包括get與set屬性用來(lái)聲明欲修飾的象屬性的getter及setter
屬性描述符內(nèi)部,數(shù)據(jù)描述符與存取描述符只能存在其中之一派歌,但是不論使用哪個(gè)描述符都可以同時(shí)設(shè)置configurable屬性以及enumerable屬性弯囊。
configurable屬性用來(lái)聲明欲修飾的屬性是否能夠配置,僅有當(dāng)其值為true時(shí)胶果,被修飾的屬性才有可能能夠被刪除匾嘱,或者重新配置。
enumerable屬性用來(lái)聲明欲修飾屬性是否可以被枚舉早抠。
知道了什么是屬性描述符霎烙,我們就可以開始著手創(chuàng)建一些對(duì)象并開始配置其屬性
順便給大家推薦一個(gè)裙,它的前面是 537蕊连,中間是631悬垃,最后就是 707。想要學(xué)習(xí)前端的小伙伴可以加入我們一起學(xué)習(xí)甘苍,互相幫助尝蠕。群里每天晚上都有大神免費(fèi)直播上課,如果不是想學(xué)習(xí)的小伙伴就不要加啦载庭。(537-631-707)
二看彼、創(chuàng)建屬性不可配置不可枚舉的對(duì)象
//使用默認(rèn)值配置
(function(){
varobj?=?{};//聲明一個(gè)空對(duì)象
Object.defineProperty(obj,"key",{
value:"static"
//沒有設(shè)置?enumerable?使用默認(rèn)值?false
//沒有?configurable?使用默認(rèn)值?false
//沒有?writable?使用默認(rèn)值?false
});
console.log(obj.key);//輸出?“static”
obj.key?="new"http://嘗試修改其值,修改將失敗,因?yàn)?writable?為?false
console.log(obj.key);//輸出?“static”
obj.a?=1;//動(dòng)態(tài)添加一個(gè)屬性
for(variteminobj){//遍歷所有?obj?的可枚舉屬性
console.log(item);
}//只輸出一個(gè)?“a”?因?yàn)?“key”的?enumerable為?false
})();
//顯示配置?等價(jià)于上面
(function(){
varobj?=?{};
Object.defineProperty(obj,"key",{
enumerable:false,
configurable:false,
writable:false,
value:"static"
})
})();
//等價(jià)配置
(function(){
varo?=?{};
o.a?=1;
//等價(jià)于
Object.defineProperty(o,"a",{value:1,
writable:true,
configurable:true,
enumerable:true});
Object.defineProperty(o,"a",{value:1});
//等價(jià)于
Object.defineProperty(o,"a",{value:1,
writable:false,
configurable:false,
enumerable:false});
})();
三、Enumerable 特性
屬性特性enumerable決定屬性是否能被for...in循環(huán)或Object.keys方法遍歷得到
(function(){
varo?=?{};
Object.defineProperty(o,"a",{value:1,enumerable:true});
Object.defineProperty(o,"b",{value:2,enumerable:false});
Object.defineProperty(o,"c",{value:2});//enumerable?default?to?false
o.d?=4;//如果直接賦值的方式創(chuàng)建對(duì)象的屬性,則這個(gè)屬性的?enumerable?為?true
for(varitemino){//遍歷所有可枚舉屬性包括繼承的屬性
console.log(item);
}
console.log(Object.keys(o));//獲取?o?對(duì)象的所有可遍歷屬性不包括繼承的屬性
console.log(o.propertyIsEnumerable('a'));//true
console.log(o.propertyIsEnumerable('b'));//false
console.log(o.propertyIsEnumerable('c'));//false
})();
輸出結(jié)果如下:
四囚聚、Configurable 特性
(function(){
varo?=?{};
Object.defineProperty(o,"a",{get:function(){return1;},
configurable:false}?);
//enumerable?默認(rèn)為?false,
//value?默認(rèn)為?undefined,
//writable?默認(rèn)為?false,
//set?默認(rèn)為?undefined
//拋出異常,因?yàn)樽铋_始定義了?configurable?為?false,故后期無(wú)法對(duì)其進(jìn)行再配置
Object.defineProperty(o,"a",{configurable:true}?);
//拋出異常,因?yàn)樽铋_始定義了?configurable?為?false,故后期無(wú)法對(duì)其進(jìn)行再配置,enumerable?的原值為?false
Object.defineProperty(o,"a",{enumerable:true}?);
//拋出異常,因?yàn)樽铋_始定義了?configurable?為?false,set的原值為?undefined
Object.defineProperty(o,"a",{set:function(val){}}?);
//拋出異常,因?yàn)樽铋_始定義了?configurable?為?false,故無(wú)法進(jìn)行覆蓋,盡管想用一樣的來(lái)覆蓋
Object.defineProperty(o,"a",{get:function(){return1}});
//拋出異常靖榕,因?yàn)樽铋_始定義了?configurable?為?false,故無(wú)法將其進(jìn)行重新配置把屬性描述符從存取描述符改為數(shù)據(jù)描述符
Object.defineProperty(o,"a",{value:12});
console.log(o.a);//輸出1
deleteo.a;//想要?jiǎng)h除屬性,將失敗
console.log(o.a);//輸出1
})();
五、提高及擴(kuò)展
1.屬性描述符中容易被誤導(dǎo)的地方之writable與configurable
(function(){
varo?=?{};
Object.defineProperties(o,{
"a":?{
value:1,
writable:true,//可寫
configurable:false//不可配置
//enumerable?默認(rèn)為?false?不可枚舉
},
"b":{
get:function(){
returnthis.a;
},
configurable:false
}
});
console.log(o.a);//1
o.a?=2;//修改值成功,writable?為?true
console.log(o.a);//2
Object.defineProperty(o,"a",{value:3});//同樣為修改值成功
console.log(o.a);//3
//將其屬性?b?的屬性描述符從存取描述符重新配置為數(shù)據(jù)描述符
Object.defineProperty(o,"b",{value:3});//拋出異常,因?yàn)?configurable?為?false
})();
2.通過上面的學(xué)習(xí)靡挥,我們都知道傳遞屬性描述符參數(shù)時(shí)序矩,是定義一個(gè)匿名的對(duì)象,里面包含屬性描述符內(nèi)容跋破,若每定義一次便要?jiǎng)?chuàng)建一個(gè)匿名對(duì)象傳入簸淀,將會(huì)造成內(nèi)存浪費(fèi)瓶蝴。故優(yōu)化如下:
(function(){
varobj?=?{};
//回收同一對(duì)象,即減少內(nèi)存浪費(fèi)
functionwithValue(value){
vard?=?withValue.d?||(
withValue.d?=?{
enumerable:false,
configurable:false,
writable:false,
value:null
}
);
d.value?=?value;
returnd;
}
Object.defineProperty(obj,"key",withValue("static"))
})();