在日常開發中,我們經常使用console.log來列印資料進行調試,但是console.log的輸出值有時候也不完全準确,或者說并不會達到我們預期的效果
舉個例子(1):
var obj = {}
obj.a = 3;
console.log(obj); // {a:3}
當我們在script中編寫如下代碼,浏覽器中會照常列印出我們所期望的資料
但是如果我們這樣呢(2):
var obj = {}
queueMicrotask(()=>{
obj.a = 3;
});
console.log(obj);
當我們在浏覽器中檢視的時候,我們會看到這樣的景象:

但是當我們展開之後呢?
?這是為什麼?
這就和console.log的列印機制有關系了
1、案例(1)中代碼是這樣運作的:
- 我們建立了一個對象,并且這個對象在記憶體中的引用位址指派給了obj
- 然後我們修改了obj.a = 3,記憶體位址中的a的值會被設定為3
- 這個時候,我們使用console.log(obj),console.log會根據obj儲存的記憶體位址去找到obj,最後列印
- 在浏覽器中正常輸出
2、案例(2)中代碼是這樣運作的:
- 我們建立了一個對象,并且這個對象在記憶體中的引用位址指派給了obj
- 然後解析到了queueMicrotask函數,将回調函數添加到了微任務隊列中,等待同步代碼執行完成,JS引擎才會執行微任務隊列中的回調
- console.log(obj),根據obj的記憶體位址去尋找,由于我們修改obj.a的代碼為一個微任務,是以其實obj現在是一個空對象,再加上我們是立刻執行的一個console.log(),那麼控制台将會輸出一個空對象{}
- 同步代碼執行完成
- 開始執行微任務隊列,執行obj.a = 3,obj記憶體位址中的a被修改為3
- 這個時候我們去檢視控制台,我們就會看到列印的是那個空對象,因為我們列印的其實是console.log的快照!
- 當我們點選控制台的展開的時候,浏覽器又會根據obj儲存的記憶體位址去尋找堆記憶體中的值,最後,我們就會看到一個被異步(微任務)修改的a:3,
其實總結起來很簡單:
javascript的異步代碼會被添加到任務隊列中(宏/微),當我們修改值的代碼寫在一個異步函數内,console.log不會等待你的異步函數執行完成,它是優先執行的,然後輸出空,然後你的異步函數又修改了obj的記憶體位址中的a,但是console.log已經執行完成了,不會重複執行,是以你看到的還是一個{}對象,但是當我們點選展開的時候,浏覽器又會根據Obj的記憶體位址去找到他的值,最後列印,但是這個時候,你的obj中的a已經被修改為了3,是以會列印3