接下來繼續(xù)討論對象的主要內(nèi)容
一、屬性描述符
對于對象中的屬性,我們一般都是直接調(diào)用,但是屬性本身又有什么特性呢,該怎么去描述?屬性描述符就扮演了這樣一種角色。屬性描述符是在ES5以后才有的,看下面的代碼。
? var myobjec = {
? ? ? a:2,
? ? ? };
? ? ? var descriptor = Object.getOwnPropertyDescriptor(myobjec,"a")
? ? ? console.log(descriptor);
? ? ? //返回的結(jié)果為一個object對象:
{
configurable:true
enumerable:true
value:2
writable:true
}
返回的對象中包含了四個描述符:configurable(可配置)、enumerable(可枚舉)、value(值)、writable(可寫入)。
這些屬性描述符可以修改嗎?當(dāng)然,我們可以用Object.defineProperty(...)來添加一個新的屬性或是修改一個已有屬性。
1)writable
決定了屬性是否可以被修改。如果將其設(shè)置為false,那么對應(yīng)的value是不能被修改的。
? ? ? var myobject = {
? ? ? ? ? a:2,
? ? ? };
? ? ? Object.defineProperty(myobject,"a",{
? ? ? writable:false,
? ? ? configurable:true,
? ? ? enumerable:true,
? ? ? value:2
? ? ? })
? ? ? myobject.a = 4;
? ? ? console.log(myobject.a);//2
2)configurable(可配置)
只要是可配置,就可以用defineProperty來修改屬性描述符。
? ? ? var myobject = {
? ? ? ? ?a:2,
? ? ? };
? ? ? myobject.a = 3;
? ? ? console.log(myobject.a ); //3
? ? ? Object.defineProperty(myobject,"a",{
? ? ? writable:true,
? ? ? configurable:false,
? ? ? enumerable:true,
? ? ? value:4
? ? ? });
? ? ? console.log(myobject.a);//4
? ? ? myobject.a = 5;
? ? ? console.log(myobject.a);//5
? ? ? Object.defineProperty(myobject,"a",{
? ? ? writable:false,
? ? ? configurable:false,
? ? ? enumerable:true,
? ? ? value:6
? ? ? });? //Uncaught TypeError
如果將configurable設(shè)置為false后,再去配置就會報“未捕獲類型”錯誤。
3)enumerable(可枚舉)
通常屬性的這個描述符默認(rèn)為true,比如在for循環(huán)中,便可以枚舉此屬性。如果你在循環(huán)遍歷中不想讓該屬性出現(xiàn),那么將它的configurable設(shè)置為false即可。
二、屬性不可變性
很多時候,你都希望屬性是不可變的,在ES5中可以通過很多方法來實現(xiàn):
1)將屬性描述符writable和configurable設(shè)置為false即可;
2)禁用:可以使用Object.preventExtensions(..)來禁止一個對象添加新屬性并且保留已有屬性;
? ? var myobject = {
? ? ? ?a:2,
? ? ? };
? ? ? Object.preventExtensions(myobject);
? ? ? myobject.b = 3;
? ? ? console.log(myobject.b);//undefined
3)密封:Object.seal(...),調(diào)用這個方法時會調(diào)用preventExtention,并把所有的屬性都標(biāo)記為configurable:false,什么意思呢?就是說調(diào)用密封方法后,任何為對象添加、刪除新屬性的操作都是不允許的,但是屬性值可以被修改,
4)凍結(jié):Object.freeze(),這個方法會調(diào)用seal方法,同時將writable標(biāo)記為false,所以這個不能修改的級別就比較高了,不僅不能添加刪除,而且也不能修改了。
三、[[get]] 和? [[put]]操作
在訪問對象屬性時,表面上我們只是獲取了屬性值這么簡單,其實是在對象上進(jìn)行了[[get]]操作,對象的內(nèi)置方法 [[get]] 會查找對象中是否存在改屬性,如果存在返回屬性對應(yīng)的值,如果不存在,其實會執(zhí)行另外一種行為,就是查找[[ prototype ]]鏈,也就是原型鏈,這個后面會講到。
如果無論如何都找不到這個屬性,那么就會返回undefined。
? ? ? var myobject = {
? ? ? a:2,
? ? ? };
? ? ? console.log(myobject.b);//undefined
? ? ? console.log(c); //Uncaught ReferenceError: c is not defined
注意屬性訪問和變量訪問的區(qū)別,如果對象中不存在屬性,那么返回undefined,而變量在當(dāng)前作用域中不存在的話,就會拋出 referenceError錯誤。原因就是屬性訪問調(diào)用了對象內(nèi)置操作[[get]]。
對于[[put]]操作,你可能認(rèn)為一般是在屬性賦值時被調(diào)用,但實際情況并不完全這樣,觸發(fā)的原因有很多。不過首先要看對象中是否存在這個屬性,如果存在的話,那么一般會檢查下面的內(nèi)容:
1)屬性是否是屬性描述符,如果是并且存在setter,就會調(diào)用setter,什么是setter呢,接下來會講到;
2)屬性描述符writable是否是false,如果是的話,在非嚴(yán)格模式下靜默失敗,在嚴(yán)格模式下拋出typeError錯誤;
3)如果上述情況都不是,那么將該值設(shè)置為屬性值。
如果對象中不存在這個屬性,那么[[put]]操作會查找原型鏈,這個后面會講到。
四、getter 和? setter 操作
ES5中的getter和setter操作會改變對象屬性的默認(rèn)操作,但是只能應(yīng)用在單個屬性上而不是整個對象上。有的同學(xué)會問到,平時好像沒有看到這兩個方法?。窟@是因為它們都是隱藏函數(shù),都是在屬性的獲取和賦值時默默的調(diào)用的。
看下面這段代碼:
var myobject = {
? ? ? get a(){
? ? ? return 2;
? ? ? }
? ? ? };
? ? ? console.log(myobject.a);//2
? ? ? Object.defineProperty(myobject,'b',{
? ? ? get:function(){
? ? ? return this.a*2;
? ? ? }
? ? ? })
? ? ? console.log(myobject.b);//4
上面兩種定義getter的方法都使得在訪問屬性時調(diào)用了getter。不過既然有了getter,那么setter也不能少對吧。
var myobject = {
? ? ? get a(){
? ? ? ? ? ?return this._a;
? ? ? },
? ? ? set a(val){?
? ? ? ? ? ? this._a = val*2;
? ? ? ? ? ?}
? ? ? };? ?
? ? ? myobject.a = 2;
? ? ? console.log(myobject.a); //4
到此為止,同上篇文章一起對對象的一些基本屬性做了系列的討論和講解,相信勤奮好學(xué)的同學(xué)們有了更深刻的認(rèn)識吧。