进入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

相关文章

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

Vue3绑定props默认值问题

《Vue3绑定props默认值问题》使用Vue3的defineProps配合TypeScript的interface定义props类型,并通过withDefaults设置默认值,使组件能安全访问传入的... 目录前言步骤步骤1:使用 defineProps 定义 Props步骤2:设置默认值总结前言使用T

从基础到高级详解Python数值格式化输出的完全指南

《从基础到高级详解Python数值格式化输出的完全指南》在数据分析、金融计算和科学报告领域,数值格式化是提升可读性和专业性的关键技术,本文将深入解析Python中数值格式化输出的相关方法,感兴趣的小伙... 目录引言:数值格式化的核心价值一、基础格式化方法1.1 三种核心格式化方式对比1.2 基础格式化示例

Web服务器-Nginx-高并发问题

《Web服务器-Nginx-高并发问题》Nginx通过事件驱动、I/O多路复用和异步非阻塞技术高效处理高并发,结合动静分离和限流策略,提升性能与稳定性... 目录前言一、架构1. 原生多进程架构2. 事件驱动模型3. IO多路复用4. 异步非阻塞 I/O5. Nginx高并发配置实战二、动静分离1. 职责2

解决升级JDK报错:module java.base does not“opens java.lang.reflect“to unnamed module问题

《解决升级JDK报错:modulejava.basedoesnot“opensjava.lang.reflect“tounnamedmodule问题》SpringBoot启动错误源于Jav... 目录问题描述原因分析解决方案总结问题描述启动sprintboot时报以下错误原因分析编程异js常是由Ja

GO语言中函数命名返回值的使用

《GO语言中函数命名返回值的使用》在Go语言中,函数可以为其返回值指定名称,这被称为命名返回值或命名返回参数,这种特性可以使代码更清晰,特别是在返回多个值时,感兴趣的可以了解一下... 目录基本语法函数命名返回特点代码示例命名特点基本语法func functionName(parameters) (nam

Python Counter 函数使用案例

《PythonCounter函数使用案例》Counter是collections模块中的一个类,专门用于对可迭代对象中的元素进行计数,接下来通过本文给大家介绍PythonCounter函数使用案例... 目录一、Counter函数概述二、基本使用案例(一)列表元素计数(二)字符串字符计数(三)元组计数三、C

MySQL 表空却 ibd 文件过大的问题及解决方法

《MySQL表空却ibd文件过大的问题及解决方法》本文给大家介绍MySQL表空却ibd文件过大的问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录一、问题背景:表空却 “吃满” 磁盘的怪事二、问题复现:一步步编程还原异常场景1. 准备测试源表与数据

解决Nginx启动报错Job for nginx.service failed because the control process exited with error code问题

《解决Nginx启动报错Jobfornginx.servicefailedbecausethecontrolprocessexitedwitherrorcode问题》Nginx启... 目录一、报错如下二、解决原因三、解决方式总结一、报错如下Job for nginx.service failed bec

SysMain服务可以关吗? 解决SysMain服务导致的高CPU使用率问题

《SysMain服务可以关吗?解决SysMain服务导致的高CPU使用率问题》SysMain服务是超级预读取,该服务会记录您打开应用程序的模式,并预先将它们加载到内存中以节省时间,但它可能占用大量... 在使用电脑的过程中,CPU使用率居高不下是许多用户都遇到过的问题,其中名为SysMain的服务往往是罪魁