深入探讨防抖函数中的 this 上下文
深入剖析防抖函数中的 this
上下文
最近我在研究防抖函数实现的时候,发现一个耗费脑子的问题,出现了令我困惑的问题。接下来,我将通过代码示例,深入探究这些现象背后的原理。
示例代码
function debounce(fn, delay) {let timer = null;console.log(1, 'this:', this);return function(...args) {console.log(2, 'this:', this);if (timer) clearTimeout(timer);timer = setTimeout(() => {console.log(3, 'this:', this); fn.apply(this, args);}, delay);};
}function debounce2(fn, delay) { let timer = null;console.log(11, 'this:', this);return function(...args) { console.log(22, 'this:', this);if (timer) clearTimeout(timer);timer = setTimeout(function() {console.log(33, 'this:', this);fn.apply(this, args);}, delay);};
}let obj = {name: 'John',sayName: function() {console.log(this.name);},debouncedSayName: debounce(function() {this.sayName();}, 300),debouncedSayName2: debounce2(function() {this.sayName();}, 300)
};obj.debouncedSayName();
obj.debouncedSayName2();
现象与问题
运行上述代码后(假设在浏览器环境中),会得到如下输出:
这里有一个顺序问题让我困惑了一下:
- 顺序问题:为什么
1
打印完成后没有紧接着打印2
,而是先打印了11
?并且为什么输出顺序不是2 -> 3 -> 22 -> 33
呢?
为了彻底搞清楚这些现象,下面是对知识点的解析。
关键知识点解析
1. 函数定义时的 this
(打印 1
和 11
处)
在 debounce
和 debounce2
函数的定义里,console.log(1, 'this:', this)
和 console.log(11, 'this:', this)
中的 this
指向取决于函数的调用方式。由于这两个函数是直接定义在全局作用域下的,并没有通过某个对象来调用,所以在 JavaScript 中,this
默认指向全局对象 Window
(在 Node.js 环境中则是 global
)。
2. 内部返回函数中的 this
(打印 2
和 22
处)
在 return
的匿名函数中,console.log(2, 'this:', this)
和 console.log(22, 'this:', this)
里的 this
指向是由调用时的上下文决定的。
当我们执行 obj.debouncedSayName()
和 obj.debouncedSayName2()
时,这两个函数是作为 obj
对象的方法被调用的。根据 JavaScript 的规则,当函数作为对象的方法调用时,this
会指向调用该方法的对象,所以这两个地方的 this
都指向 obj
。
3. 定时器中的 this
(打印 3
和 33
处)
- 箭头函数中的
this
(console.log(3, 'this:', this)
):在debounce
函数的定时器回调中使用了箭头函数。箭头函数有一个重要的特性,就是它不会绑定自己的this
,而是继承自定义它的外部函数(这里是匿名函数)的this
。因此,这里的this
仍然指向obj
。 - 普通函数中的
this
(console.log(33, 'this:', this)
):在debounce2
函数的定时器回调中使用的是普通函数。普通函数的this
在调用时会动态绑定,而在定时器中,普通函数的this
默认指向全局对象Window
(在 Node.js 环境中是timeout
)。
顺序问题解析
观察输出顺序:1 -> 11 -> 2 -> 22 -> 3 -> 33
为什么不是 1 -> 2
?
当我们定义 obj
对象时,会通过 debounce
和 debounce2
函数生成 debouncedSayName
和 debouncedSayName2
属性。在这个过程中,debounce
和 debounce2
函数会被调用,于是就会执行到 console.log(1, 'this:', this)
和 console.log(11, 'this:', this)
。而 debounce
和 debounce2
返回的内部函数,要等到调用 obj.debouncedSayName()
和 obj.debouncedSayName2()
时才会执行。
为什么不是 2 -> 3 -> 22 -> 33
?
JavaScript 是单线程的编程语言,而 setTimeout
是异步操作。当遇到 setTimeout
时,它会将回调函数推入事件队列,等待主线程的同步任务全部执行完毕后再执行。因此,debouncedSayName
和 debouncedSayName2
的同步部分会先执行,分别打印 2
和 22
,然后才会将定时器的回调函数放入事件队列。最后,定时器的回调函数依次执行,分别打印 3
和 33
。
总结
通过这个例子,我们可以得出以下重要结论:
1. 箭头函数与普通函数的区别
- 箭头函数:箭头函数中的
this
继承自外层函数,这使得它非常适合用于需要保留上下文的场景。 - 普通函数:普通函数的
this
根据调用方式动态绑定,在不同的调用场景下,this
的指向可能会发生变化。
2. 异步任务的执行顺序
异步任务会被推入事件队列,只有当主线程的同步任务全部完成后,才会依次执行事件队列中的异步任务。
3. this
的指向受调用方式影响
如果函数作为对象的方法调用,this
会指向该对象;如果函数没有通过对象调用,this
通常指向全局对象(在严格模式下为 undefined
)。
希望通过这篇文章,你能更清晰地理解防抖函数中的 this
机制,在实际开发中避免因 this
指向问题而产生的错误。
相关文章:

深入探讨防抖函数中的 this 上下文
深入剖析防抖函数中的 this 上下文 最近我在研究防抖函数实现的时候,发现一个耗费脑子的问题,出现了令我困惑的问题。接下来,我将通过代码示例,深入探究这些现象背后的原理。 示例代码 function debounce(fn, delay) {let time…...

【AI论文】魔鬼在细节:关于在训练专用混合专家模型时实现负载均衡损失
摘要:本文重新审视了在训练混合专家(Mixture-of-Experts, MoEs)模型时负载均衡损失(Load-Balancing Loss, LBL)的实现。具体来说,MoEs的LBL定义为N_E乘以从1到N_E的所有专家i的频率f_i与门控得分平均值p_i的…...
Gurobi基础语法之addVar 和 addVars
addVar 和 addVars作为 Gurobi模型对象中的方法,常常用来生成变量,本文介绍了Python中的这两个接口的使用 addVar addVar(lb0.0, ubfloat(inf), obj0.0, vtypeGRB.CONTINUOUS, name, columnNone) lb 和 ub让变量在生成的时候就有下界和上届,…...
C语言学习阶段性总结(五)---函数
函数构成五要素: 1、返回值类型 2、函数名 3、参数列表(输入) 4、函数体 (算法) 5、返回值 (输出) 返回值类型 函数名 (参数列表) { 函数体; return 返回值; } void 类型…...

K8S 快速实战
K8S 核心架构原理: 我们已经知道了 K8S 的核心功能:自动化运维管理多个容器化程序。那么 K8S 怎么做到的呢?这里,我们从宏观架构上来学习 K8S 的设计思想。首先看下图: K8S 是属于主从设备模型(Master-Slave 架构),即有 Master 节点负责核心的调度、管理和运维,Slave…...

java后端之事务管理
Transactional注解:作用于业务层的方法、类、接口上,将当前方法交给spring进行事务管理,执行前开启事务,成功执行则提交事务,执行异常回滚事务 spring事务管理日志: 默认情况下,只有出现Runti…...

【Redis】缓存+分布式锁
目录 缓存 Redis最主要的使用场景就是作为缓存 缓存的更新策略: 1.定期生成 2.实时生成 面试重点: 缓存预热(Cache preheating): 缓存穿透(Cache penetration) 缓存雪崩 (Cache avalan…...
二分查找题目:寻找两个正序数组的中位数
文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题:寻找两个正序数组的中位数 出处:4. 寻找两个正序数组的中位数 难度 8 级 题目描述 要求 给定两个大…...

网络安全 | F5-Attack Signatures详解
关注:CodingTechWork 关于攻击签名 攻击签名是用于识别 Web 应用程序及其组件上攻击或攻击类型的规则或模式。安全策略将攻击签名中的模式与请求和响应的内容进行比较,以查找潜在的攻击。有些签名旨在保护特定的操作系统、Web 服务器、数据库、框架或应…...

Redis --- 分布式锁的使用
我们在上篇博客高并发处理 --- 超卖问题一人一单解决方案讲述了两种锁解决业务的使用方法,但是这样不能让锁跨JVM也就是跨进程去使用,只能适用在单体项目中如下图: 为了解决这种场景,我们就需要用一个锁监视器对全部集群进行监视…...
LeetCode100之全排列(46)--Java
1.问题描述 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案 示例1 输入:nums [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 示例2 输入:nums [0,1] 输出…...
goframe 博客分类文章模型文档 主要解决关联
goframe 博客文章模型文档 模型结构 (BlogArticleInfoRes) BlogArticleInfoRes 结构体代表系统中的一篇博客文章,包含完整的元数据和内容管理功能。 type BlogArticleInfoRes struct {Id uint orm:"id,primary" json:"id" …...

【JavaWeb06】Tomcat基础入门:架构理解与基本配置指南
文章目录 🌍一. WEB 开发❄️1. 介绍 ❄️2. BS 与 CS 开发介绍 ❄️3. JavaWeb 服务软件 🌍二. Tomcat❄️1. Tomcat 下载和安装 ❄️2. Tomcat 启动 ❄️3. Tomcat 启动故障排除 ❄️4. Tomcat 服务中部署 WEB 应用 ❄️5. 浏览器访问 Web 服务过程详…...

安卓日常问题杂谈(一)
背景 关于安卓开发中,有很多奇奇怪怪的问题,有时候这个控件闪一下,有时候这个页面移动一下,这些对于快速开发中,去查询,都是很耗费时间的,因此,本系列文章,旨在记录安卓…...

Kitchen Racks 2
Kitchen Racks 2 吸盘置物架 Kitchen Racks-CSDN博客...
嵌入式学习笔记-杂七杂八
文章目录 连续波光纤耦合激光器工作原理主要特点应用领域设计考虑因素 数值孔径(Numerical Aperture,简称NA)数值孔径的定义数值孔径的意义数值孔径的计算示例数值孔径与光纤 四象限探测器检测目标方法四象限划分检测目标的步骤1. 数据采集2.…...

14-7C++STL的stack容器
(一)stack容器的入栈与出栈 (1)stack容器的简介 stack堆栈容器,“先进后出”的容器,且stack没有迭代器 (2)stack对象的默认构造 stack采用模板类实现,stack对象的默认…...
Vue 3 中的响应式系统:ref 与 reactive 的对比与应用
Vue 3 的响应式系统是其核心特性之一,它允许开发者以声明式的方式构建用户界面。Vue 3 引入了两种主要的响应式 API:ref 和 reactive。本文将详细介绍这两种 API 的用法、区别以及在修改对象属性和修改整个对象时的不同表现,并提供完整的代码…...

物业巡更系统助推社区管理智能化与服务模式创新的研究与应用
内容概要 在现代社区管理中,物业巡更系统扮演着至关重要的角色。首先,我们先来了解一下这个系统的概念与发展背景。物业巡更系统,顾名思义,是一个用来提升物业管理效率与服务质量的智能化工具。随着科技的发展,传统的…...
windows蓝牙驱动开发-生成和发送蓝牙请求块 (BRB)
以下过程概述了配置文件驱动程序生成和发送蓝牙请求块 (BRB) 应遵循的一般流程。 BRB 是描述要执行的蓝牙操作的数据块。 生成和发送 BRB 分配 IRP。 分配BRB,请调用蓝牙驱动程序堆栈导出以供配置文件驱动程序使用的 BthAllocateBrb 函数。;初始化 BRB…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...

【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !
我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...