进入debugger调试时,this 输出 undefined的问题,箭头函数与babel造成的调试不便

您所在的位置:网站首页 dev如何调试函数内部 进入debugger调试时,this 输出 undefined的问题,箭头函数与babel造成的调试不便

进入debugger调试时,this 输出 undefined的问题,箭头函数与babel造成的调试不便

2024-06-07 00:51| 来源: 网络整理| 查看: 265

进入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,接着看完吧) 监听,比如下面的例子。 添加Watch监听 这里因为我测试的例子很简单,所以这里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上回答一个问题的答案,有兴趣的同学可以点进去看。再加上国内复刻网站的中文链接。

以上解释如果描述不当或欠缺,欢迎指正,谢谢。



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3