深入探讨防抖函数中的 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. 顺序问题:为什么 1 打印完成后没有紧接着打印 2,而是先打印了 11 ?并且为什么输出顺序不是 2 -> 3 -> 22 -> 33 呢?

为了彻底搞清楚这些现象,下面是对知识点的解析。

关键知识点解析

1. 函数定义时的 this(打印 111 处)

debouncedebounce2 函数的定义里,console.log(1, 'this:', this)console.log(11, 'this:', this) 中的 this 指向取决于函数的调用方式。由于这两个函数是直接定义在全局作用域下的,并没有通过某个对象来调用,所以在 JavaScript 中,this 默认指向全局对象 Window(在 Node.js 环境中则是 global)。

2. 内部返回函数中的 this(打印 222 处)

return 的匿名函数中,console.log(2, 'this:', this)console.log(22, 'this:', this) 里的 this 指向是由调用时的上下文决定的。
当我们执行 obj.debouncedSayName()obj.debouncedSayName2() 时,这两个函数是作为 obj 对象的方法被调用的。根据 JavaScript 的规则,当函数作为对象的方法调用时,this 会指向调用该方法的对象,所以这两个地方的 this 都指向 obj

3. 定时器中的 this(打印 333 处)

  • 箭头函数中的 thisconsole.log(3, 'this:', this):在 debounce 函数的定时器回调中使用了箭头函数。箭头函数有一个重要的特性,就是它不会绑定自己的 this,而是继承自定义它的外部函数(这里是匿名函数)的 this。因此,这里的 this 仍然指向 obj
  • 普通函数中的 thisconsole.log(33, 'this:', this):在 debounce2 函数的定时器回调中使用的是普通函数。普通函数的 this 在调用时会动态绑定,而在定时器中,普通函数的 this 默认指向全局对象 Window(在 Node.js 环境中是 timeout)。

顺序问题解析

观察输出顺序:1 -> 11 -> 2 -> 22 -> 3 -> 33

为什么不是 1 -> 2

当我们定义 obj 对象时,会通过 debouncedebounce2 函数生成 debouncedSayNamedebouncedSayName2 属性。在这个过程中,debouncedebounce2 函数会被调用,于是就会执行到 console.log(1, 'this:', this)console.log(11, 'this:', this)。而 debouncedebounce2 返回的内部函数,要等到调用 obj.debouncedSayName()obj.debouncedSayName2() 时才会执行。

为什么不是 2 -> 3 -> 22 -> 33

JavaScript 是单线程的编程语言,而 setTimeout 是异步操作。当遇到 setTimeout 时,它会将回调函数推入事件队列,等待主线程的同步任务全部执行完毕后再执行。因此,debouncedSayNamedebouncedSayName2 的同步部分会先执行,分别打印 222,然后才会将定时器的回调函数放入事件队列。最后,定时器的回调函数依次执行,分别打印 333

总结

通过这个例子,我们可以得出以下重要结论:

1. 箭头函数与普通函数的区别

  • 箭头函数:箭头函数中的 this 继承自外层函数,这使得它非常适合用于需要保留上下文的场景。
  • 普通函数:普通函数的 this 根据调用方式动态绑定,在不同的调用场景下,this 的指向可能会发生变化。

2. 异步任务的执行顺序

异步任务会被推入事件队列,只有当主线程的同步任务全部完成后,才会依次执行事件队列中的异步任务。

3. this 的指向受调用方式影响

如果函数作为对象的方法调用,this 会指向该对象;如果函数没有通过对象调用,this 通常指向全局对象(在严格模式下为 undefined)。

希望通过这篇文章,你能更清晰地理解防抖函数中的 this 机制,在实际开发中避免因 this 指向问题而产生的错误。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/960872.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【PostgreSQL内核学习 —— (WindowAgg(一))】

WindowAgg 窗口函数介绍WindowAgg理论层面源码层面WindowObjectData 结构体WindowStatePerFuncData 结构体WindowStatePerAggData 结构体eval_windowaggregates 函数update_frameheadpos 函数 声明:本文的部分内容参考了他人的文章。在编写过程中,我们尊…

RocketMQ消息是如何存储的?

大家好,我是锋哥。今天分享关于【RocketMQ消息是如何存储的?】面试题。希望对大家有帮助; RocketMQ消息是如何存储的? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 RocketMQ 使用了一个高性能、分布式的消息存储架构…

MongoDB平替数据库对比

背景 项目一直是与实时在线监测相关,特点数据量大,读写操作大,所以选用的是MongoDB。但按趋势来讲,需要有一款国产数据库可替代,实现信创要求。选型对比如下 1. IoTDB 这款是由清华大学主导的开源时序数据库&#x…

电力晶体管(GTR)全控性器件

电力晶体管(Giant Transistor,GTR)是一种全控性器件,以下是关于它的详细介绍:(模电普通晶体管三极管进行对比学习) 基本概念 GTR是一种耐高电压、大电流的双极结型晶体管(BJT&am…

蓝桥杯python语言基础(4)——基础数据结构(上)

目录 一、列表与元组 (一)列表 (二)操作列表 (三)元组 习题P502 习题P497 二、字符串 (一)字符串的基本操作 (二)字符串的常用方法 (三&…

langchain基础(三)

Chain: 关于三个invoke: 提示模板、聊天模型和输出解析器都实现了langchain的runnable接口, 都具有invoke方法(因为invoke方法是Runnable的通用调用方法) 所以可以一次性调用多次invoke直接得到最终结果:…

数据分析和AI丨应对AI实施挑战,工程领域AI应用的五大方法

工程领域的人工智能 (AI) 已经开始发挥价值,低代码和无代码工具正在使曾经仅属于专业数据科学家的 AI 能力变得大众化。 然而,并非工程领域的每个人都能从中受益,使用新的便捷的 AI 工具提高工作效率并不难&#xff0c…

【Linux探索学习】第二十七弹——信号(一):Linux 信号基础详解

Linux学习笔记: https://blog.csdn.net/2301_80220607/category_12805278.html?spm1001.2014.3001.5482 前言: 前面我们已经将进程通信部分讲完了,现在我们来讲一个进程部分也非常重要的知识点——信号,信号也是进程间通信的一…

games101-(5/6)

光栅化 投影完成之后,视图区域被确定在从[-1,1]的单位矩阵中,下一步就是光栅化 长宽比:ratio 垂直的可视角度:fild-of-view 可以看到的y 轴的范围,角度越小 越接近正交投影 屏幕坐标系 、 将多边形转化成像素 显示…

Linux之详谈——权限管理

目录 小 峰 编 程 ​编辑 一、权限概述 1、什么是权限 2、为什么要设置权限 3、Linux中的权限类别- 4、Linux中文件所有者 1)所有者分类(谁) 2)所有者的表示方法 ① u(the user who owns it)(属主权限&…

oracle比较一下统计信息差异吧

统计信息发生了哪些变化? 从上次收集到最近一次收集有什么不同? set long 999999 longc 99999 line 100 select report, maxdiffpct from table(dbms_stats.diff_table_stats_in_history(SYS,T1,to_timestamp(2025-01-22 09:01:46,YYYY-MM-DD hh24:mi:s…

【现代深度学习技术】深度学习计算 | 参数管理

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈PyTorch深度学习 ⌋ ⌋ ⌋ 深度学习 (DL, Deep Learning) 特指基于深层神经网络模型和方法的机器学习。它是在统计机器学习、人工神经网络等算法模型基础上,结合当代大数据和大算力的发展而发展出来的。深度学习最重…

用WinForm如何制作简易计算器

首先我们要自己搭好页面 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms;namespace _7_简易计算…

spring入门其实特别简单,也可以做单机。

直接在官网配置 点右边的"ADD DEPENDENCIES",选“Spring Web” 然后点“GENERATE”,会自动下载一个demo.zip。解压demo.zip,用IntelliJ IDEA打开就能用了。 IntelliJ IDEA打开之后,你得配置你的

智慧消防营区一体化安全管控 2024 年度深度剖析与展望

在 2024 年,智慧消防营区一体化安全管控领域取得了令人瞩目的进展,成为保障营区安全稳定运行的关键力量。这一年,行业在政策驱动、技术创新应用、实践成果及合作交流等方面呈现出多元且深刻的发展态势,同时也面临着一系列亟待解决…

C++封装红黑树实现mymap和myset和模拟实现详解

文章目录 map和set的封装map和set的底层 map和set的模拟实现insertiterator实现的思路operatoroperator- -operator[ ] map和set的封装 介绍map和set的底层实现 map和set的底层 一份模版实例化出key的rb_tree和pair<k,v>的rb_tree rb_tree的Key和Value不是我们之前传统意…

1. Java-MarkDown文件创建-工具类

Java-MarkDown文件创建-工具类 1. 思路 根据markdown语法&#xff0c;拼装markdown文本内容 2. 工具类 import java.util.Arrays; import java.util.List;/*** Markdown生成工具类* Author: 20004855* Date: 2021/1/15 16:00*/ public class MarkdownGenerator {private Str…

虚拟机中的IP地址总是变化怎么办

1、问题概述&#xff1f; 在虚拟机中安装的centos或者redhat&#xff0c;默认情况下使用的都是dbcp模式&#xff0c;会自动的获取ip地址。 每次重启虚拟机后&#xff0c;可能都会使用不同的ip地址。 如何需要使用固定ip&#xff0c;就需要手动修改。 本文测试系统RedHat7.9…

物业管理系统推动社区智能化与服务创新的未来发展路径

内容概要 随着物业管理行业的不断发展&#xff0c;物业管理系统也逐渐成为社区管理的重要组成部分。它不仅能够显著提高服务效率&#xff0c;还带来了很多创新的服务模式&#xff0c;这些都让生活变得更加便利。首先&#xff0c;物业管理系统通过在线收费功能&#xff0c;可以…

AI如何帮助解决生活中的琐碎难题?

引言&#xff1a;AI已经融入我们的日常生活 你有没有遇到过这样的情况——早上匆忙出门却忘了带钥匙&#xff0c;到了公司才想起昨天的会议资料没有打印&#xff0c;或者下班回家还在纠结晚饭吃什么&#xff1f;这些看似微不足道的小事&#xff0c;往往让人疲惫不堪。而如今&a…