对 fn.apply(this, arguments) 的使用还在疑惑?快进来看看它的设计含义及常见使用场景吧~

🙌 如文章有误,恳请评论区指正,谢谢!
❤ 写作不易,「点赞」+「收藏」+「转发」 谢谢支持!

背景

近期在研究高阶函数封装的过程中,看到 fn.apply(this, arguments) 的出镜率非常高,而如果对于不了解该用法的初学者来说,可能直接写成 fn() 是更好理解的,但为什么不局限于此,而是要再上升到 fn.apply(this, arguments) 的使用呢?此篇文章便来带你一探究竟~

原文链接:对 fn.apply(this, arguments) 的使用还在疑惑?快进来看看它的设计含义及常见使用场景吧~

apply 的基本介绍

我们先来看看 MDN - Function.prototype.apply 对于 apply 函数的介绍:

在这里插入图片描述

其实简单来说就是,apply 函数可以改变使用者的身份,例如如下例子中,obj1 对象有一个方法 func1,那想调用该方法就直接 obj1.func1() 即可。而 obj2 对象中没有方法 func1 但又想使用该方法,那可以通过 apply 来“借用”,即 obj1.func1.apply(obj2),此时就相当于括号中的 obj2 来调用该函数了!

const obj1 = {
    func1: function() { console.log(this, this.data) },
    data: "obj1"
}

const obj2 = {
    data: "obj2"
};

obj1.func1();
obj1.func1.apply(func2); // 借 obj1 的 func1 用一下

代码调用如下图:

在这里插入图片描述

括号中的 arguments 含义

我们先来看看 MDN - arguments 对于 arguments 类数组的介绍:

在这里插入图片描述

arguments 是一个对应于传递给函数的参数的类数组对象,即我们可以不用显式地在函数入参处声明变量,例如 func1(num1, num2, s3) 来获得变量,而是通过 arguments 获得参数数组即可

const func1 = function() {
    console.log("传入函数的参数", arguments);
}

func1(1, 2, "test");

代码调用如下图:

在这里插入图片描述

开始介绍 fn.apply(this, arguments)

先举一个代码的场景作为例子:

const obj1 = {
    func1: function(fn) {
        fn.apply(this, arguments);
        // fn();
    }
}

const test = function() { console.log(this, arguments) };
obj1.func1(test, 1, 2);

1. 由此我们可以大概推出什么逻辑?

当我们通过 obj1.func1() 调用函数时,此时 func1 中的 this 指向即为 obj1,而如果我传参进 func1 时,arguments 即为这些传入的参数。

2. 那跟直接 fn() 调用的区别在哪呢?

作用域的区别!

fn.apply(this, arguments) 调用的结果如图:

在这里插入图片描述

可以看到 test() 中打印出的 this 指向是 obj1,即我通过 obj1 来调用自己的 func1 函数,通过 fn.apply(this, arguments) 可以保留我的 this 指向,让我在第三方函数中调用时的 this 作用域仍保留是 obj1



fn() 调用的结果如图:

在这里插入图片描述

可以看到 test() 中打印出的 this 指向变成了 Windowthis 作用域消失了,变成 Window 了!

原因:如果你直接调用 fn(),而 fntest 函数定义在了 Window 作用域中,因此相当于由 Window 调用,因此 test 函数内部指向就变成了 Window

3. 结论

因此,我们可以知道,通过使用 fn.apply(this, arguments) 代替 fn(),我们可以保留函数作用域,使得原本谁调用该函数,那经过中间函数包裹处理后,还是他来继续调用该函数。

常见使用场景

当你需要通过高阶函数来二次处理一个普通函数时,因为高阶函数特性就是经过高阶函数处理后,不改变原函数的任何性质,包括函数作用域函数所传参数,而防抖和节流就是常见使用场景之二。

1. 防抖(debounce) 函数

function debounce (fn, wait, immediate) {
  let timer;
  return function () {
    // 谁调用返回的这个函数,那 this 指向就是指向谁
    if (immediate) fn.apply(this, arguments); // 立即执行

    if (timer) clearTimeout(timer); // 如果debounce不再触发,那么 setTimeout 过 wait 会自动执行,但如果再次触发了就要清空计时器,重新计时
    
    timer = setTimeout(() => {
      fn.apply(this, arguments);
      timer && clearTimeout(timer);
      timer = null;
    }, wait);
    
  }
}

function log () {
  console.log("a");
  console.log(this); // 保留了this指向,比如只有 obj 中才有 a,才能正确打印
}

const obj = {
  a: 1,
  debounceObj: debounce(log, 2000) // 绑定在了obj内
}
const debounceWindow = debounce(log, 350, false);

for (let i = 1; i <= 10; i++) {
  setTimeout(obj.debounceObj, i * 100); // 相当于每 100ms 都会触发一次防抖函数,都会导致 350ms 的计时器重新计时,之后最后一次会打印出'a'     
}

2. 节流(throttle) 函数

let count = 0;

function throttle (fn, wait) {
  let prev = new Date();
  const that1 = this;
  return function () {
    // 谁调用返回的这个函数,那 this 指向就是指向谁
    let now = new Date();
    // 如果超出了需等待的 wait,那就可以执行啦总算
    if (now - prev > wait) {
      // fn();
      fn.apply(this, arguments);
      prev = new Date();
    }
  }
}

// const throttleTest = throttle(test, 2000);
const obj = {
  a: 1,
  throttleTestObj: throttle(test, 2000) // 绑定在了obj内
}

const throttleTestWindow = throttle(test, 2000); // 绑定在了window

function test () {
  count++;
  console.log("打印this环境", this, count); // 保留了this指向,比如只有 obj 中才有 a,才能正确打印

};

setInterval(() => {
  obj.throttleTestObj();
}, 500)

通过在防抖和节流函数中使用 fn.apply(this, arguments),保留了原函数 fn 正确的 this 指向,比如只有 obj 中才有 a,因此才能正确打印,即没改变作用域!



最后

我是 Smoothzjc,致力于产出更多且不仅限于前端方面的优质文章

大家也可以关注我的公众号 @ Smooth前端成长记录,及时通过移动端获取到最新文章消息!

写作不易,「点赞」+「收藏」+「转发」 谢谢支持❤

往期推荐

《requestAnimationFrame 与 setInterval 究竟如何选择?》

《拒绝死记硬背!清晰思路讲透 控制并发数、Promise.all、Promise.race 的实现逻辑》

《手把手教前端从0到1通过 Node + Express 开发简易接口,项目开发+部署服务器(亲身痛苦经历)》

《都2022年了还不考虑来学React Hook吗?6k字带你从入门到吃透》

《一份不可多得的 Webpack 学习指南(1万字长文带你入门 Webpack 并掌握常用的进阶配置)》

《通过 React15 ~ 17 的优化迭代来简单聊聊 Fiber》

《【offer 收割机之面试必备】一篇非常全面的 从 URL 输入到页面展现的全过程 精华梳理》

《【offer 收割机之手写系列】10分钟带你掌握原理并手写防抖与节流的立即/非立即执行版本》

《Github + hexo 实现自己的个人博客、配置主题(超详细)》

《带你3分钟掌握常见的水平垂直居中面试题》

《【建议收藏】长达万字的git常用指令总结!!!适合小白及在工作中想要对git基本指令有所了解的人群》

《浅谈javascript的原型和原型链(新手懵懂想学会原型链?看这篇文章就足够啦!!!)》

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

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

相关文章

【ReactPress】React + antd + NestJS + NextJS + MySQL 的简洁兼时尚的博客网站

ReactPress 是使用React开发的开源发布平台&#xff0c;用户可以在支持React和MySQL数据库的服务器上架设属于自己的博客、网站。也可以把 ReactPress 当作一个内容管理系统&#xff08;CMS&#xff09;来使用。 前言 此项目是用于构建博客网站的&#xff0c;包含前台展示、管理…

Pycharm远程调试deepspeed!可用!

本人写代码的习惯就是一定是要从别人优秀的代码中调试学习的&#xff0c;直接运行看的话&#xff0c;可能知道了大概的逻辑但是缺无法知道细节的话&#xff08;参数的含义或者某某数据格式类型&#xff09;&#xff0c;可能对整体代码逻辑的把控不是狠好&#xff0c;所以还是从…

Redis 中 Bitmap 原理和应用

Bitmap Redis中的Bitmap&#xff08;位图&#xff09;是一种较为特殊数据类型&#xff0c;它以最小单位bit来存储数据&#xff0c;我们知道一个字节由 8个 bit 组成&#xff0c;和传统数据结构用字节存储相比&#xff0c;这使得它在处理大量二值状态&#xff08;true、false 或…

基于 STM32 的天气时钟项目中添加天气数据的网络获取功能

基于 STM32 的天气时钟项目中添加天气数据的网络获取功能&#xff0c;您需要确保您的开发环境具备网络连接能力。这里以 ESP8266 Wi-Fi 模块为例&#xff0c;详细说明如何实现网络获取天气数据的功能。 1. 硬件连接 连接 ESP8266 模块 请参考以下连接方式&#xff0c;将 ESP82…

mysql-springboot netty-flink-kafka-spark(paimon)-minio

1、下载spark源码并编译 mkdir -p /home/bigdata && cd /home/bigdata wget https://archive.apache.org/dist/spark/spark-3.4.3/spark-3.4.3.tgz 解压文件 tar -zxf spark-3.4.3.tgz cd spark-3.4.3 wget https://raw.githubusercontent.com/apache/incubator-celeb…

【Spring】体系结构

Spring框架至今集成了多个模块&#xff0c;这些模块分布在数据访问/集成&#xff08;Data Access/Integration&#xff09;、Web层、面向切面的编程&#xff08;Aspect Oriented Programming&#xff0c;AOP&#xff09;模块、植入&#xff08;Instrumentation&#xff09;模块…

软件缺陷等级评定综述

1. 前言 正确评估软件缺陷等级&#xff0c;在项目的生命周期中有着重要的作用&#xff1a; 指导缺陷修复的优先级和资源分配 在软件开发和维护过程中&#xff0c;资源&#xff08;包括人力、时间和资金&#xff09;是有限的。通过明确缺陷的危险等级&#xff0c;可以帮助团队合…

Linux:vim命令总结及环境配置

文章目录 前言一、vim的基本概念二、vim模式命令解析1. 命令模式1&#xff09;命令模式到其他模式的转换&#xff1a;2&#xff09;光标定位&#xff1a;3&#xff09;其他命令&#xff1a; 2. 插入模式3. 底行模式4. 替换模式5. 视图模式6. 外部命令 三、vim环境的配置1. 环境…

Obsidian的Git插件设置配置全流程 -- 简单的电脑端多平台同步方案及常见问题

Obsidian的Git插件设置配置全流程 -- 简单的电脑端多平台同步方案及常见问题 参考文章引言1. git 介绍及安装2. git 本地配置及远程仓库链接3. obsidian 的 git 插件4. 常用的使用场景和对应的命令4.1. 本地仓库已推送到远端&#xff0c;如何在另一个电脑上第一次同步4.2 多端同…

【优选算法篇】微位至简,数之恢宏——解构 C++ 位运算中的理与美

文章目录 C 位运算详解&#xff1a;基础题解与思维分析前言第一章&#xff1a;位运算基础应用1.1 判断字符是否唯一&#xff08;easy&#xff09;解法&#xff08;位图的思想&#xff09;C 代码实现易错点提示时间复杂度和空间复杂度 1.2 丢失的数字&#xff08;easy&#xff0…

Redis(3):持久化

一、Redis高可用概述 在介绍Redis高可用之前&#xff0c;先说明一下在Redis的语境中高可用的含义。   我们知道&#xff0c;在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务&#xff08;99.9%、99.99%、9…

高亚科技签约酸动力,助力研发管理数字化升级

近日&#xff0c;中国企业管理软件资深服务商高亚科技与广东酸动力生物科技有限公司&#xff08;以下简称“酸动力”&#xff09;正式签署合作协议。借助高亚科技的8Manage PM项目管理软件&#xff0c;酸动力将进一步优化项目过程跟踪与节点监控&#xff0c;提升研发成果的高效…

大模型领域最值得看的 9 本新书,找到了

在人工智能革命的浪潮中&#xff0c;程序员们正站在技术变革的最前沿。本书单精选了关于人工智能在各行业应用的最新著作&#xff0c;从医疗诊断到金融风控&#xff0c;从智能制造到智慧城市&#xff0c;全面展现AI如何重塑行业生态&#xff0c;推动社会进步。通过阅读这些书籍…

加入GitHub Spark需要申请

目录 加入GitHub Spark需要申请 GitHub Spark 一、产品定位与特点 二、核心组件与功能 三、支持的AI模型 四、应用场景与示例 五、未来展望 六、申请体验 加入GitHub Spark需要申请 GitHub Spark 是微软旗下GitHub在2024年10月30日的GitHub Universe大会上推出的一款革…

Rust移动开发:Rust在iOS端集成使用介绍

iOS调用Rust 上篇介绍了 Rust移动开发&#xff1a;Rust在Android端集成使用介绍, 这篇主要看下iOS上如何使用Rust&#xff0c;Rust可以给移动端开发提供跨平台&#xff0c;通用组件支持。 该篇适合对iOS、Rust了解&#xff0c;想知道如何整合调用和编译的&#xff0c;如果想要…

【月之暗面kimi-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞 …

2024 CSS保姆级教程四

CSS中的动画 CSS动画&#xff08;CSS Animations&#xff09;是为层叠样式表建议的允许可扩展标记语言&#xff08;XML&#xff09;元素使用CSS的动画的模块​ 即指元素从一种样式逐渐过渡为另一种样式的过程​ 常见的动画效果有很多&#xff0c;如平移、旋转、缩放等等&#…

[C++11] Lambda 表达式

lambda 表达式&#xff08;Lambda Expressions&#xff09;作为一种匿名函数&#xff0c;为开发者提供了简洁、灵活的函数定义方式。相比传统的函数指针和仿函数&#xff0c;lambda 表达式在简化代码结构、提升代码可读性和编程效率方面表现出色。 Lambda 表达式的基本语法 在…

AI4SCIENSE(鄂维南院士:再谈AI for Science)

鄂维南院士&#xff1a;再谈AI for Science_哔哩哔哩_bilibili 以往处理高维问题 量子力学&#xff1a;单变量乘积 统计学&#xff1a;旋转 AI4S 处理数据 蛋白质折叠&#xff1f; 不是纯粹的数据驱动 物理学等学科基本原理 例&#xff1a;分子动力学 数据模型 流程图 这…

接收nVisual中rabbitmq数据不成功问题排查

rabbitmq服务部署成功的情况下&#xff0c;消息对接不成功一般原因为消息发送失败&#xff0c;发送失败大多数可能为global_settings表配置错误。下面从两个方面解决消息对接不成功问题。 1.数据是否成功发送 检查global_settings表中rabbitmq发送消息配置信息是否正确 #MQS…