那些前端工作中遇到的坑(01)

leonsux發表於2018-12-16

前段時間接手一個老專案(jQuery+React混在一塊)的遷移工作,除了用React重寫一些老頁面(這種至少是自己寫的,心裡有數,調起來也省心),剩下的就是將原有的React遷到新倉庫裡,複製!貼上!改路徑!完事!是個體力活。

你以為這樣就結束了?一大堆bug在等著,整整改了半個月,雖然都是些小問題,但整天被測試釘來釘去還是蠻頭疼的。

現在來說說今天的主角吧,這是一個不起眼的問題,最初發現的時候很讓人摸不著頭腦。

part one

const res = request(url);
console.log(res);   // { stat: 'ok', data: { value: 1, childs: [1, 2, 3] } }
複製程式碼

上面的程式碼很普通,但是開啟控制檯的Network去看這個介面的返回結果是:{ stat: 'ok', data: { value: 1, children: [1, 2, 3] } },你沒有看錯,返回的資料中是children,而console.log列印出來的是childs,是不是很不可思議?接下來我們逐一排查問題。

part two

const res = request(url);
console.log(JSON.stringify(res));   // { stat: 'ok', data: { value: 1, children: [1, 2, 3] } }
console.log(res);   // { stat: 'ok', data: { value: 1, childs: [1, 2, 3] } }
複製程式碼

這裡我們把res轉成字串再輸出,和直接輸出對比發現轉成字串後的結果是和介面返回的結果一樣的,都是children。看到這裡相信有不少小夥伴已經清楚是怎麼回事了,但應該也有部分同學腦子裡全是問號了(比如當時的我)。

那些前端工作中遇到的坑(01)
如果光是靠這些線索,確實還沒發定位問題。那我們進一步探索,既然出問題的的是res,那就順藤摸瓜,找到使用他的地方。然後就發現了這段程式碼:

res.data.children = res.data.childs;
delete res.data.childs;
複製程式碼

原來是為了換欄位,直接操作了源資料,而物件,陣列都是引用型別的,so...

那難道console.log難道是非同步執行的嗎,不然程式碼順序執行的話前面輸出的結果應該是對的呀?

這麼說其實不準確,我們把同樣的程式碼換個方式執行,比如node.js:

那些前端工作中遇到的坑(01)

那些前端工作中遇到的坑(01)

然後跟在瀏覽器中執行對比一下:

那些前端工作中遇到的坑(01)

這樣就很清晰了,問題在於瀏覽器控制檯的輸出機制,但為什麼要這麼設計呢?在網上查閱了一番後,得出的結論是:如果順序執行console.log(輸出的內容較大,層級較深),可能會造成阻塞,影響使用者體驗,所以瀏覽器在後臺非同步處理了console.log

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

而通過JSON.stringify轉成字串再輸出相當於給res拍了一次快照(記錄了他最初的樣子),後面再操作該物件也不會影響到這個字串(你操作物件關我字串什麼事)。

問題搞清楚了,下面要考慮的就是怎麼解決以及以後如何避免。

歸根到底就是一句話:

不要操作源資料!

不要操作源資料!

不要操作源資料!

使用之前深拷貝一下,可以使用最簡單粗暴的JSON.parse(JSON.stringify(res))

雞湯:坑踩多了不要緊,反正後面還多著呢

相關文章