消失的屬性
本博客同步自我的Github博客
最近在開(kāi)發(fā)組件的過(guò)程中,需要隨時(shí)監(jiān)控整個(gè)組件對(duì)象的構(gòu)建,包括對(duì)象上的屬性方法的變更,以及原型鏈的變化。本來(lái),在測(cè)試代碼中加一個(gè)console.log:
var d = new dialog({
...
});
console.log('final object', d);
d.show();
就可以觀察最終生成的組件對(duì)象是否符合我的預(yù)期,也沒(méi)出過(guò)什么問(wèn)題,也沒(méi)理由會(huì)出現(xiàn)什么問(wèn)題,直到調(diào)試過(guò)程中出現(xiàn)了這樣的情況:
組件的配置需要將用戶傳入的配置屬性和默認(rèn)屬性進(jìn)行合并,然后需要對(duì)組件對(duì)象中的一個(gè)屬性布爾值進(jìn)行判斷后改寫組件的另一個(gè)屬性:
this.maskOpacity = this.modal ? this.maskOpacity : 0;
第一眼看上去代碼似乎沒(méi)有問(wèn)題(實(shí)際上有問(wèn)題),但是運(yùn)行時(shí)和預(yù)期行為不一致,this.modal的判定始終為false。那么我就很順手地打上一句console.log(this)來(lái)檢查對(duì)象的屬性,當(dāng)調(diào)整傳入的配置對(duì)象時(shí),生成對(duì)象上的this.modal屬性應(yīng)該會(huì)隨之變化,檢查控制臺(tái)輸出結(jié)果,this.modal的值確實(shí)是會(huì)根據(jù)參數(shù)不同而變化的。這就奇怪了,試著打斷點(diǎn)調(diào)試,結(jié)果就不一樣了,this.modal的值在執(zhí)行判斷時(shí)顯示為undefined。同樣的事情在Chrome和FF中都發(fā)生了。
這個(gè)modal屬性神秘消失了。
問(wèn)題一分為二,一個(gè)是代碼本身的邏輯問(wèn)題,經(jīng)過(guò)進(jìn)一步調(diào)試,在執(zhí)行到這句判斷的時(shí)候,配置對(duì)象中的modal屬性值還未寫入this,所以this.modal值為undefined的現(xiàn)象是正常的,通過(guò)修改this.modal為opt.modal比較輕易就解決了。第二個(gè)問(wèn)題卻是在調(diào)試過(guò)程中遇到的,console.log出的this為何能查找到本不應(yīng)存在的modal屬性,導(dǎo)致誤導(dǎo)了我的思路。借助于萬(wàn)能的StackOverflow,最后總算是找到了原因。
這段組件的代碼邏輯是比較復(fù)雜的,其中的this.modal屬性,在上文執(zhí)行判斷的語(yǔ)句處,確實(shí)應(yīng)該undefined,但是在后面的代碼中,有這樣一段賦值:
this.modal = opt.modal = true;
這段賦值直接改寫了this.modal,可為何后文的賦值會(huì)影響到前文的輸出呢?其中的原因在于,當(dāng)console.log(this)輸出時(shí),this對(duì)象在控制臺(tái)中的顯示為折疊狀態(tài),如圖:

為了查看對(duì)象中具體的屬性信息,就必須用鼠標(biāo)點(diǎn)一下展開(kāi),這個(gè)時(shí)候,JS早就執(zhí)行完畢了,this.modal的值也已改寫,在展開(kāi)this對(duì)象的時(shí)刻,this中的屬性顯示結(jié)果實(shí)際上是this對(duì)象的最終形態(tài),那么這種輸出上的“延遲”是延遲到所有JS執(zhí)行結(jié)束還是僅局限于console語(yǔ)句所在函數(shù)作用域內(nèi)呢?我們可以寫段代碼驗(yàn)證一下:
var foo = {};
function addProperty() {
foo.test1 = 'test1';
foo.test2 = 'test2';
foo.test3 = 'test3';
foo.test4 = 'test4';
foo.test5 = 'test5';
foo.test6 = 'test6';
foo.test7 = 'test7';
foo.test8 = 'test8';
foo.test9 = 'test9';
console.log(foo);
foo.bar = 'bar';
}
addProperty();
foo.baz = 'baz';
foo = null;
console.log(foo);
代碼寫得很挫,純粹是為了測(cè)試而寫了。在這里我還在最后加了一個(gè)對(duì)null的指向來(lái)銷毀對(duì)象(嚴(yán)格來(lái)說(shuō)還應(yīng)該delete掉對(duì)象的所有屬性,這里省略),控制臺(tái)中的輸出如下:

可見(jiàn),控制臺(tái)中輸出的“延遲范圍”是整個(gè)腳本代碼的范圍,不局限于某個(gè)函數(shù)作用域,同時(shí)還不受賦值為null的影響,當(dāng)然,在foo = null;之后的console的輸出就確實(shí)為null了。
同時(shí),觸發(fā)這種“延遲”現(xiàn)象還有一個(gè)必要條件,那就是輸出對(duì)象的屬性要足夠多,使得控制臺(tái)會(huì)先將對(duì)象內(nèi)容折疊起來(lái),給用戶點(diǎn)擊展開(kāi)的機(jī)會(huì),如果屬性過(guò)少,兩行就顯示完了,也就看不到這個(gè)bug了。
在各種條件的巧合作用下發(fā)現(xiàn)了這樣一個(gè)現(xiàn)象,在今后的開(kāi)發(fā)過(guò)程中要加以注意,不要受到誤導(dǎo),甚至于利用這種特性為開(kāi)發(fā)提供便捷。

浙公網(wǎng)安備 33010602011771號(hào)