定義 getter 與 setter
1.通過對象初始化器在創(chuàng)建對象的時候指明(也可以稱為通過字面值創(chuàng)建對象時聲明)
(function(){
varo?=?{
a:7,
get?b(){returnthis.a?+1;},//通過?get,set的?b,c方法間接性修改?a?屬性
set?c(x){this.a?=?x/2}
};
console.log(o.a);
console.log(o.b);
o.c?=50;
console.log(o.a);
})();
在 chrome 中調(diào)試視圖如下:
可以看到對象下多了?get?屬性以及?set?屬性
當(dāng)然?get?語句與?set?語句可以聲明多次用來對應(yīng)多個?getter?和?setter
使用這種方法的好處是可以在聲明屬性的時候同時聲明對應(yīng)的?getter?和?setter
這里就有人問了提茁,能不能將o 對象的?get?及?set?方法的方法名都改成 “a”,這樣就可以直接通過“.”來訪問方法直接操作
(function(){
varo?=?{
a:7,
get?a(){returnthis.a?+1;},//死循環(huán)
set?a(x){this.a?=?x/2}
};
console.log(o.a);
console.log(o.b);
o.c?=50;
console.log(o.a);
})();
打開 chrome 查看創(chuàng)建后的視圖如下:?
可以看到這個時候的get與set方法已經(jīng)和上面不同,但是是否真的能起作用呢,答案是否定的,當(dāng)我們通過o.a調(diào)用的是get語句 聲明的 a方法,進(jìn)入到該方法后遇到this.a方法繼續(xù)調(diào)用該方法形成死循環(huán)最終導(dǎo)致死循環(huán)報內(nèi)存溢出錯誤剩晴。
新語法(ES6):暫時只有 firefox 支持,其他瀏覽器會報錯
(function(){
varb?="bb";
varc?="cc";
varo?=?{
a:7,
get?[b](){returnthis.a?+1;},
set?[c](x){this.a?=?x/2},
};
console.log(o.a);
console.log(o[b]);
o["cc"]?=50;
console.log(o.a);
})();
打開 firefox 查看調(diào)試:?
輸出結(jié)果如下:
2.使用?Object.create?方法
引用 MDN:
概述 Object.create() 方法創(chuàng)建一個擁有指定原型和若干個指定屬性的對象。 語法 Object.create(proto, [ propertiesObject ])
我們都知道使用?Object.create?方法傳遞一個參數(shù)的時候可以創(chuàng)建一個以該參數(shù)為原型的對象载碌。
第二個參數(shù)是可選項,是一個匿名的參數(shù)對象衅枫,該參數(shù)對象是一組屬性與值嫁艇,該對象的屬性名稱將是新創(chuàng)建的對象的屬性名稱,值是屬性描述符(包擴(kuò)數(shù)據(jù)描述符或存取描述符弦撩,具體解釋看后面的內(nèi)容 什么是屬性描述符)步咪。
通過屬性描述符我們可以實現(xiàn)為新創(chuàng)建的對象添加?get?方法以及?set?方法
(function(){
varo?=null;
o?=Object.create(Object.prototype,//指定原型為?Object.prototype
{
bar:{
get:function(){
return10;
},
set:function(val){
console.log("Setting?`o.bar`?to?",val);
}
}
}//第二個參數(shù)
);
console.log(o.bar);
o.bar?=12;
})();
在 chrome 中調(diào)試試圖如下:?
可以看到新創(chuàng)建對象通用多了?get?以及?set?屬性。
輸出結(jié)果如下:?
上面這個例子并沒有用來針對的?get?方法以及?set?方法使用的屬性
(function(){
varo?=null;
o?=Object.create(Object.prototype,//指定原型為?Object.prototype
{
bar:{
get:function(){
returnthis.a;
},
set:function(val){
console.log("Setting?`o.bar`?to?",val);
this.a?=?val;
},
configurable:true
}
}//第二個參數(shù)
);
o.a?=10;
console.log(o.bar);
o.bar?=12;
console.log(o.bar);
})();
亦或:
(function(){
varo?=?{a:10};
o?=Object.create(o,//指定原型為?o?這里實際可以理解為繼承
{
bar:{
get:function(){
returnthis.a;
},
set:function(val){
console.log("Setting?`o.bar`?to?",val);
this.a?=?val;
},
configurable:true
}
}//第二個參數(shù)
);
console.log(o.bar);
o.bar?=12;
console.log(o.bar);
})();
輸出結(jié)果如下:
使用這種方式的好處是可配置性高益楼,但初學(xué)者容易迷糊猾漫。
3.使用?Object.defineProperty?方法
引用 MDN:
概要 Object.defineProperty() 方法直接在一個對象上定義一個新屬性,或者修改一個已經(jīng)存在的屬性感凤, 并返回這個對象悯周。 語法 Object.defineProperty(obj, prop, descriptor) 參數(shù) obj 需要定義屬性的對象。 prop 需被定義或修改的屬性名陪竿。 descriptor 需被定義或修改的屬性的描述符禽翼。
(function?()?{??var?o?=?{?a?:?1}//聲明一個對象,包含一個?a?屬性,值為1
??Object.defineProperty(o,"b",{????get:?function?()?{??????return?this.a;
????},????set?:?function?(val)?{??????this.a?=?val;
????},????configurable?:?true
??});?
??console.log(o.b);
??o.b?=?2;??console.log(o.b);
})();
這個方法與前面兩種的區(qū)別是:使用前面兩種只能在聲明定義的時候指定getter與setter,使用該方法可以隨時的添加或修改族跛。
如果說需要一次性批量添加 getter 與 setter 也是沒問題的闰挡,使用如下方法:
4.使用?Object.defineProperties方法
MDN:
概述 Object.defineProperties() 方法在一個對象上添加或修改一個或者多個自有屬性,并返回該對象礁哄。 語法 Object.defineProperties(obj, props) 參數(shù) obj 將要被添加屬性或修改屬性的對象 props 該對象的一個或多個鍵值對定義了將要為對象添加或修改的屬性的具體配置
不難看出用法與Object.defineProperty方法類似
(function(){
varobj?=?{a:1,b:"string"};
Object.defineProperties(obj,{
"A":{
get:function(){returnthis.a+1;},
set:function(val){this.a?=?val;}
},
"B":{
get:function(){returnthis.b+2;},
set:function(val){this.b?=?val}
}
});
console.log(obj.A);
console.log(obj.B);
obj.A?=3;
obj.B?="hello";
console.log(obj.A);
console.log(obj.B);
})();
輸出結(jié)果如下:
5.使用?Object.prototype.__defineGetter__?以及?Object.prototype.__defineSetter__??方法
(function(){
varo?=?{a:1};
o.__defineGetter__("giveMeA",function(){
returnthis.a;
});
o.__defineSetter__("setMeNew",function(val){
this.a?=?val;
})
console.log(o.giveMeA);
o.setMeNew?=2;
console.log(o.giveMeA);
})();
輸出結(jié)果為1和2 查看 MDN 有如下說明:?
順便給大家推薦一個裙长酗,它的前面是 537,中間是631姐仅,最后就是 707花枫。想要學(xué)習(xí)前端的小伙伴可以加入我們一起學(xué)習(xí),互相幫助掏膏。群里每天晚上都有大神免費直播上課劳翰,如果不是想學(xué)習(xí)的小伙伴就不要加啦。(537-631-707)
二馒疹、什么是屬性描述符佳簸?
MDN:
對象里目前存在的屬性描述符有兩種主要形式:數(shù)據(jù)描述符和存取描述符。
數(shù)據(jù)描述符是一個擁有可寫或不可寫值的屬性。
存取描述符是由一對 getter-setter 函數(shù)功能來描述的屬性生均。
描述符必須是兩種形式之一听想;不能同時是兩者。
數(shù)據(jù)描述符和存取描述符均具有以下可選鍵值:
value 與屬性相關(guān)的值马胧『郝颍可以是任何有效的 JavaScript 值(數(shù)值,對象佩脊,函數(shù)等)蛙粘。默認(rèn)為 undefined。 writable true 當(dāng)且僅當(dāng)可能用 賦值運(yùn)算符 改變與屬性相關(guān)的值威彰。默認(rèn)為 false出牧。
存取描述符同時具有以下可選鍵值:
get
一個給屬性提供 getter 的方法,如果沒有 getter 則為 undefined歇盼。方法將返回用作屬性的值舔痕。默認(rèn)為 undefined。 set 一個給屬性提供 setter 的方法豹缀,如果沒有 setter 則為 undefined伯复。該方法將收到作為唯一參數(shù)的新值分配給屬性。默認(rèn)為 undefined耿眉。
以上是摘自MDN的解釋边翼,看起來是很晦澀的,具體什么意思呢: 首先我們從以上解釋知道該匿名參數(shù)對象有個很好聽的名字叫屬性描述符鸣剪,屬性描述符又分成兩大塊:數(shù)據(jù)描述符以及存取描述符(其實只是一個外號组底,給指定的屬性集合起個外號)。
數(shù)據(jù)描述符包括兩個屬性 :value屬性以及writable屬性筐骇,第一個屬性用來聲明當(dāng)前欲修飾的屬性的值债鸡,第二個屬性用來聲明當(dāng)前對象是否可寫即是否可以修改
存取描述符就包括get與set屬性用來聲明欲修飾的象屬性的getter及setter
屬性描述符內(nèi)部,數(shù)據(jù)描述符與存取描述符只能存在其中之一铛纬,但是不論使用哪個描述符都可以同時設(shè)置configurable屬性以及enumerable屬性厌均。
configurable屬性用來聲明欲修飾的屬性是否能夠配置,僅有當(dāng)其值為true時告唆,被修飾的屬性才有可能能夠被刪除棺弊,或者重新配置。
enumerable屬性用來聲明欲修飾屬性是否可以被枚舉擒悬。
知道了什么是屬性描述符模她,我們就可以開始著手創(chuàng)建一些對象并開始配置其屬性
三、創(chuàng)建屬性不可配置不可枚舉的對象
//使用默認(rèn)值配置
(function(){
varobj?=?{};//聲明一個空對象
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://嘗試修改其值,修改將失敗,因為?writable?為?false
console.log(obj.key);//輸出?“static”
obj.a?=1;//動態(tài)添加一個屬性
for(variteminobj){//遍歷所有?obj?的可枚舉屬性
console.log(item);
}//只輸出一個?“a”?因為?“key”的?enumerable為?false
})();
//顯示配置?等價于上面
(function(){
varobj?=?{};
Object.defineProperty(obj,"key",{
enumerable:false,
configurable:false,
writable:false,
value:"static"
})
})();
//等價配置
(function(){
varo?=?{};
o.a?=1;
//等價于
Object.defineProperty(o,"a",{value:1,
writable:true,
configurable:true,
enumerable:true});
Object.defineProperty(o,"a",{value:1});
//等價于
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)建對象的屬性,則這個屬性的?enumerable?為?true
for(varitemino){//遍歷所有可枚舉屬性包括繼承的屬性
console.log(item);
}
console.log(Object.keys(o));//獲取?o?對象的所有可遍歷屬性不包括繼承的屬性
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
//拋出異常,因為最開始定義了?configurable?為?false,故后期無法對其進(jìn)行再配置
Object.defineProperty(o,"a",{configurable:true}?);
//拋出異常,因為最開始定義了?configurable?為?false,故后期無法對其進(jìn)行再配置,enumerable?的原值為?false
Object.defineProperty(o,"a",{enumerable:true}?);
//拋出異常,因為最開始定義了?configurable?為?false,set的原值為?undefined
Object.defineProperty(o,"a",{set:function(val){}}?);
//拋出異常,因為最開始定義了?configurable?為?false,故無法進(jìn)行覆蓋,盡管想用一樣的來覆蓋
Object.defineProperty(o,"a",{get:function(){return1}});
//拋出異常,因為最開始定義了?configurable?為?false,故無法將其進(jìn)行重新配置把屬性描述符從存取描述符改為數(shù)據(jù)描述符
Object.defineProperty(o,"a",{value:12});
console.log(o.a);//輸出1
deleteo.a;//想要刪除屬性,將失敗
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});//拋出異常,因為?configurable?為?false
})();
2.通過上面的學(xué)習(xí)畜侦,我們都知道傳遞屬性描述符參數(shù)時元扔,是定義一個匿名的對象,里面包含屬性描述符內(nèi)容旋膳,若每定義一次便要創(chuàng)建一個匿名對象傳入澎语,將會造成內(nèi)存浪費。故優(yōu)化如下:
(function(){
varobj?=?{};
//回收同一對象,即減少內(nè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"))
})();