console.log()眼見不爲實的原因

所有結果的運行環境都爲Chrome,不同的瀏覽器運行結果可能不同

眼見不爲實

第一行打出來是Obj,點開後發現居然有屬性a且值爲"賦值後"
第二行打印出屬性a的值,卻是Undefined

        let test = {}
        console.log(test)
        console.log(test.a)
        test.a="賦值後"

image

深入分析console.log(Object)

爲什麼會出現眼見不爲實的情況

如果我們打印一個對象,chrome默認會省略具體信息,只顯示一個Object,這是引用的快照,而不是快照的內容
你不點開,引擎是不會去這個引用的地址去取內容的

        let obj = {
            a:"a",
            b:{
                bb:'bb'
            },
            timeOut:"now"
        }
        console.log(obj)
        console.log(obj.a)
        console.log(JSON.stringify(obj))
        
        obj.a="a修改後"
        obj.b.bb="bb修改後"
        
        console.log(obj)

image

當我們點擊這個object展開的時候,chrome會重新獲取這些引用的值
但當我們點擊object去展開的時候,代碼已經執行完了,於是重新獲取的是修改過的值
實際上相當於一種懶加載,這也是爲什麼你可以在控制檯無限查看Object的prototype了。

image

setTimeout驗證Chrome取值模式

下面的代碼會有兩種不同輸出,只取決於你點開打印出來的Object的時間

        let obj = {
            timeOut:"now"
        }
        
        console.log(obj)
        console.log(obj)
        console.log(obj.timeOut)
        
        
        setTimeout(()=>{
            obj.timeOut="五秒後"
        },5000)
        

第一種情況爲你不等待,直接點擊展開兩個Object,timeOut都爲now,即使你等了5後折上再展開去拉取值,值再也不會改變了,chrome應該會存儲這次的拉取過的數據,不會再改變了,
image

第二種情況是再展開第二個Object的時候我等了5秒鐘再展開
image

獲取準確的對象信息-console.log(JSON.stringify(obj))

這種方式能強制打出現在的結果,
把對象序列化到一個字符串中,以強制執行一次“快照”

console.log是異步或是同步的說法是不準確的

這不是異步和同步的問題,console並不是JavaScript提供的對象,而是由宿主環境(請參考本書的“類型和語法”部分)添加到JavaScript 中的。這具體到不同的瀏覽器,比如Chrome中是由Devtool的控制檯提供,Firefox中是由Firebug的控制檯提供。
image

《你不知道的javascript中卷》中的部分具體說明

並沒有什麼規範或一組需求指定console.* 方法中如何工作——它們並不是JavaScript 正式
的一部分,而是由宿主環境(請參考本書的“類型和語法”部分)添加到JavaScript 中的。因此,不同的瀏覽器和JavaScript 環境可以按照自己的意願來實現,有時候這會引起混淆。

尤其要提出的是,在某些條件下,某些瀏覽器的console.log(..) 並不會把傳入的內容立即輸出。出現這種情況的主要原因是,在許多程序(不只是JavaScript)中,I/O 是非常低速的阻塞部分。所以,(從頁面/UI 的角度來說)瀏覽器在後臺異步處理控制檯I/O 能夠提高性能,這時用戶甚至可能根本意識不到其發生。

下面這種情景不是很常見,但也可能發生,從中(不是從代碼本身而是從外部)可以觀察到這種情況:
PS:可以試試這個

var a = {
    index: 1
};
// 然後
console.log( a ); // ??
// 再然後
a.index++;

我們通常認爲恰好在執行到console.log(..) 語句的時候會看到a 對象的快照,打印出類
似於{ index: 1 } 這樣的內容,然後在下一條語句a.index++ 執行時將其修改,這句的執
行會嚴格在a 的輸出之後。

多數情況下,前述代碼在開發者工具的控制檯中輸出的對象表示與期望是一致的。

但是,這段代碼運行的時候,瀏覽器可能會認爲需要把控制檯I/O 延遲到後臺,在這種情況下,
等到瀏覽器控制檯輸出對象內容時,a.index++ 可能已經執行,因此會顯示{ index: 2 }。

到底什麼時候控制檯I/O 會延遲,甚至是否能夠被觀察到,這都是遊移不定的。

如果在調試的過程中遇到對象在console.log(..) 語句之後被修改,可你卻看到了意料之外的結果,要意識到這可能是這種I/O 的異步化造成的。

如果遇到這種少見的情況,最好的選擇是在JavaScript 調試器中使用斷點,而不要依賴控制檯輸出。次優的方案是把對象序列化到一個字符串中,以強制執行一次“快照”,比如通過JSON.stringify(..)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章