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

本文主要是介绍进入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对象。这就是进入断点在控制台中输出thisundefined 的第一个问题。

至于出现原因就是因为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上回答一个问题的答案,有兴趣的同学可以点进去看。再加上国内复刻网站的中文链接。

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

这篇关于进入debugger调试时,this 输出 undefined的问题,箭头函数与babel造成的调试不便的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/747239

相关文章

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Kotlin 作用域函数apply、let、run、with、also使用指南

《Kotlin作用域函数apply、let、run、with、also使用指南》在Kotlin开发中,作用域函数(ScopeFunctions)是一组能让代码更简洁、更函数式的高阶函数,本文将... 目录一、引言:为什么需要作用域函数?二、作用域函China编程数详解1. apply:对象配置的 “流式构建器”最

SpringBoot启动报错的11个高频问题排查与解决终极指南

《SpringBoot启动报错的11个高频问题排查与解决终极指南》这篇文章主要为大家详细介绍了SpringBoot启动报错的11个高频问题的排查与解决,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一... 目录1. 依赖冲突:NoSuchMethodError 的终极解法2. Bean注入失败:No qu

MySQL新增字段后Java实体未更新的潜在问题与解决方案

《MySQL新增字段后Java实体未更新的潜在问题与解决方案》在Java+MySQL的开发中,我们通常使用ORM框架来映射数据库表与Java对象,但有时候,数据库表结构变更(如新增字段)后,开发人员可... 目录引言1. 问题背景:数据库与 Java 实体不同步1.1 常见场景1.2 示例代码2. 不同操作

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

如何解决mysql出现Incorrect string value for column ‘表项‘ at row 1错误问题

《如何解决mysql出现Incorrectstringvalueforcolumn‘表项‘atrow1错误问题》:本文主要介绍如何解决mysql出现Incorrectstringv... 目录mysql出现Incorrect string value for column ‘表项‘ at row 1错误报错

如何解决Spring MVC中响应乱码问题

《如何解决SpringMVC中响应乱码问题》:本文主要介绍如何解决SpringMVC中响应乱码问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Spring MVC最新响应中乱码解决方式以前的解决办法这是比较通用的一种方法总结Spring MVC最新响应中乱码解

pip无法安装osgeo失败的问题解决

《pip无法安装osgeo失败的问题解决》本文主要介绍了pip无法安装osgeo失败的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 进入官方提供的扩展包下载网站寻找版本适配的whl文件注意:要选择cp(python版本)和你py

C++中::SHCreateDirectoryEx函数使用方法

《C++中::SHCreateDirectoryEx函数使用方法》::SHCreateDirectoryEx用于创建多级目录,类似于mkdir-p命令,本文主要介绍了C++中::SHCreateDir... 目录1. 函数原型与依赖项2. 基本使用示例示例 1:创建单层目录示例 2:创建多级目录3. 关键注

解决Java中基于GeoTools的Shapefile读取乱码的问题

《解决Java中基于GeoTools的Shapefile读取乱码的问题》本文主要讨论了在使用Java编程语言进行地理信息数据解析时遇到的Shapefile属性信息乱码问题,以及根据不同的编码设置进行属... 目录前言1、Shapefile属性字段编码的情况:一、Shp文件常见的字符集编码1、System编码