進入debugger調試時,this 輸出 undefined的問題,箭頭函數與babel造成的調試不便


 進入debugger調試時, this 輸出 undefined的問題,箭頭函數與babel造成的調試不便

引言
問題區分
1.箭頭函數內的 this 和封閉的局部變量一樣
2.箭頭函數內的 this 被babel 打包后重命名了
3.正確獲取this 解決方案
引言
  之前用VUE開發的時候經常遇到,用 chrome 的調試工具進入頁面 debugger 的時候,用 console.log(this) 能輸出 this的值。但是在斷點過程中,用鼠標移動到 this 上顯示的確是 undefined(在控制台中輸出 this 也是 undefined)。說實話,當時是因為影響並不大,也沒在意,也沒探究過具體的原因。昨天剛好手上任務完成,就抽了一些時間去仔細找找具體的原因以及解決方案。

問題區分
  對的,你沒看錯,這個問題要區分一下,因為這個問題並不只是一個問題。這里涉及到多個問題,我在查找原因的時候就發現有人問類似的問題。當我知道具體原因后就發現,問的以及回答的存在牛頭不對馬嘴的情況。

1.箭頭函數內的 this 和封閉的局部變量一樣
  這里不展開分享箭頭函數,主要講一點,箭頭函數里的 this 跟封閉的局部變量一樣,如果箭頭函數內部未顯示的寫出 this,進入這個箭頭函數內部的斷點,this 輸出的是 undefined,看下面這個例子你就知道了。

這個動圖寫了兩個例子,一個箭頭函數內只寫了一個debugger ,另一個還顯示的寫了this,都進入斷點時,第一個輸出undefined,第二個輸出了Window對象。這就是進入斷點在控制台中輸出this 為 undefined 的第一個問題。

至於出現原因就是因為chrome調試器的優化,如果未在函數內部引用局部變量(這里是this),這個變量就不會存儲在此函數上下文對象中。所以總結就是箭頭函數內部的this(這里不談指向),生存周期與普通函數的封閉局部變量一樣,都是未顯示引用輸出就是undefined(針對chrome 調試,火狐不會)。

有興趣的小伙伴可以進入這篇 Chrome調試器為何認為封閉的局部變量未定義?中看看其他牛人的討論,如果英語足夠好也可以進原英文鏈接 Why does Chrome debugger think closed local variable is undefined? 相信這里能完全解決你此問題的疑惑。


2.箭頭函數內的 this 被babel 打包后重命名了
剛了解到這個問題的時候就去babel官網看了,找到 Why is this being remapped to undefined? 這樣一個問題,我興奮的以為,我找到了答案。但被事實狠狠打大了一把臉。這里問的主要是因為 babel ES2015模塊是隱式嚴格模式的,所以即使是上方第一個問題用普通函數輸出也是undefined(嚴格模式下用window. 調用函數,函數內部this 才會指向 Window 對象)。

回到我們的具體問題。進入斷點時 console.log(this) 輸出了內容,而直接在控制台寫 this 執行或者鼠標移到斷點處的 this 上顯示 undefined是什么原因(這里不是探究為什么顯示undefined了,而是為什么和代碼中console.log(this) 輸出的不一致,即使解決了輸出undefined ,也就是移除嚴格模式,這里的this 應該也只是輸出 Window對象,而不是我們當前運行環境中的比如Vue 這個組件對象)。

因為在項目中使用了babel。比如箭頭函數就會被打包成普通函數,而this 指向就會用變量保存來代替,比如_this,_this2之類的。
我把代碼例子貼出來大家就知道了,我用的vue 就用vue使用的一個箭頭函數的例子解釋。

/* 這個代碼是vue methods 鈎子下的一個函數,是我的源代碼。*/
handle() {
this.add().then(() => {
console.log(this.number);
debugger;
});
}

/* 這個代碼就是上方代碼在項目運行中,打包后的代碼 */
handle: function handle() {
var _this2 = this;

this.add().then(function () {
console.log(_this2.number);
debugger;
});
}

下面的截圖就是在運行中Sources 下進入斷點的代碼

從上面明顯可以看到,這里的this 已經在babel 打包后賦值給了_this2這個變量。意思就是雖然我們斷點進入的是比如上方的About.vue 這個文件,實際運行的代碼是左側這個cjs.js? 這個文件。這種運行環境下你能看到 Console 下 直接寫this 輸出是undefined,而在About.vue 這個文件中console.log(this.number) 實際是cjs.js 這個文件中的 console.log(_this2.number) 輸出的。

這里為什么進入斷點時在.vue文件中,實際是在.js文件呢,是因為vue 配置webpack 的 源映射 source-map 的默認配置。默認配置在打包速度上稍慢,但是勝在調試更加方便。也可以改成其他配置,點擊上面的鏈接可以進入官網查看詳細配置,這里就不談了。

.vue 就是斷點這里this沒有指向值,如果想調試查看你想要的 this 值,可以在cjs.js這個文件里看,不過因為打包后和實際寫的源代碼有較多差異你也可以在Watch 下添加_this2 (為什么是_this2,接着看完吧) 監聽,比如下面的例子。

這里因為我測試的例子很簡單,所以這里this 是用變量_this2保存的。babel 都是用_this 開頭的變量保存的 this,所以大家可以在自己項目中多嘗試一下,因為這個具體賦值到this?上根據項目代碼場景確定的。

也可以像我這樣,進入斷點時在控制台輸入 _this 這里提示我 是 _this6,如果實在不找不到就接着看下面。


3.正確獲取this 解決方案
說到底難道沒有不添加Watch 的辦法嗎,而且這里還是不能把鼠標移動到this 上提示預期值,其實也是有一些比較婉轉的解決方案的。

第一個,如果項目不用向下兼容,那么推薦不要使用babel了,嘿嘿,這個簡單粗暴。(以下動圖演示能看到這里的運行代碼就沒被babel 打包,因為我把babel 移除了)

但是,既然你能遇到這個問題,肯定是項目中需要使用babel 的,那么我們用一個插件來解決一下。

npm i babel-plugin-transform-es2015-arrow-functions --save-dev

然后在.babelrc或者是babel.config.js 配置文件中加入

plugins: [["transform-es2015-arrow-functions", { spec: true }]]

運行你的代碼,進入斷點就會發現。

項目確實被babel 打包了,但是箭頭函數編譯方式跟之前不一樣了,之前是使用變量保存的方式,現在是使用bind 的方式。也就是內部函數this 的值被更改為外部函數this 值了。這樣就可以直接在斷點處查看this 的期望值,以后調試前端代碼也能更加方便。雖然此方法獲取來源的提供者說並非在所有的地方都行之有效,但經測試,我在最新構建的Vue項目中以及以前老的項目中都能使用。如果有遇到不能使用的情況,歡迎反饋哈。

此方法是參考 loganfsmyth 在Stack Overflow上回答一個問題的答案,有興趣的同學可以點進去看。再加上國內復刻網站的中文鏈接。

 from:https://blog.csdn.net/rudy_zhou/article/details/105278657


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM