有時(shí)候調(diào)試代碼,發(fā)現(xiàn)所看的結(jié)果與期望的有差異,誤導(dǎo)了我們的判斷误趴,找錯了方向,耽誤了很多時(shí)間务傲,console.log()
的輸出竟然會出現(xiàn)異步輸出的情況凉当,因而所以這里記錄一下遇到的這個(gè)問題,加深印象售葡。
chrome 瀏覽器測試
可以看出看杭,當(dāng) console.log(obj.per)
看到的還是未修改的 vv
,一旦展開卻變成了 呱呱
挟伙,為什么會有這個(gè)異常輸出
原因:
這里不得不提到 js 的對象是引用類型楼雹,每次使用對象時(shí)候,都只是引用了對象在堆中的引用像寒,當(dāng)修改了 obj.per.name 時(shí)候烘豹,也修改了堆中引用的 name,當(dāng) console.log(obj.per)
打印的是對象當(dāng)時(shí)的快照信息诺祸,當(dāng)展開對象時(shí)候携悯,會去內(nèi)存讀對象的屬性值。
為什么開發(fā)者工具有這個(gè)表現(xiàn)筷笨?
《你不知道的javascript中卷》第二部分異步和性能1.1節(jié)異步控制臺部分有提及:
翻譯:并沒有什么規(guī)范或一組需求指定console.* 方法族如何工作——它們并不是JavaScript 正式的一部分憔鬼,而是由宿主環(huán)境(請參考本書的“類型和語法”部分)添加到JavaScript 中的。因此胃夏,不同的瀏覽器和JavaScript 環(huán)境可以按照自己的意愿來實(shí)現(xiàn)轴或,有時(shí)候這會引起混淆。
尤其要提出的是仰禀,在某些條件下照雁,某些瀏覽器的console.log(..) 并不會把傳入的內(nèi)容立即輸出。出現(xiàn)這種情況的主要原因是答恶,在許多程序(不只是JavaScript)中饺蚊,I/O 是非常低速的阻塞部分萍诱。所以,(從頁面/UI 的角度來說)瀏覽器在后臺異步處理控制臺I/O 能夠提高性能污呼,這時(shí)用戶甚至可能根本意識不到其發(fā)生裕坊。
書中還舉了一個(gè)例子
var a = { index: 1};// 然后console.log( a ); // ??// 再然后a.index++;
類似的,當(dāng)執(zhí)行輸出 a 時(shí)燕酷,會顯示 a 的快照籍凝,而 a.index ++ 的確嚴(yán)格執(zhí)行在 console.log 之后,但當(dāng)你展開 對象 a 時(shí)候苗缩,會去內(nèi)存中去讀取 a.index 值饵蒂,瀏覽器可能會認(rèn)為需要把控制臺I/O 延遲到后臺,這時(shí)候可能修改成了 2挤渐。
到底什么時(shí)候控制臺I/O 會延遲苹享,甚至是否能夠被觀察到双絮,這都是游移不定的浴麻。
所以如果在調(diào)試的過程中遇到對象在console.log(..) 語句之后被修改,可你卻看到了意料之外的結(jié)果囤攀,要意識到這可能是這種I/O 的異步化造成的软免。
書中建議:
如果遇到這種少見的情況,最好的選擇是在JavaScript 調(diào)試器中使用斷點(diǎn)焚挠,而不要依賴控制臺輸出膏萧。次優(yōu)的方案是把對象序列化到一個(gè)字符串中,以強(qiáng)制執(zhí)行一次“快照”蝌衔,比如通過JSON.stringify(..)榛泛。
結(jié)論:
由此可見,console.log打印出來的內(nèi)容并不是一定百分百可信的內(nèi)容噩斟。一般對于基本類型number曹锨、string、boolean剃允、null沛简、undefined
的輸出是可信的。但對于Object
等引用類型來說斥废,則就會出現(xiàn)上述異常打印輸出椒楣。所以對于一般基本類型的調(diào)試,調(diào)試時(shí)使用console.log來輸出內(nèi)容時(shí)牡肉,不會存在坑捧灰。但調(diào)試對象時(shí),最好還是使用打斷點(diǎn)(debugger
)這樣的方式來調(diào)試更好统锤。
文獻(xiàn)來源:https://github.com/Mmzer/think/issues/30