JS 手写call、apply和bind方法

2023-09-21 09:53:11

一、方法介绍

apply、call和bind都是系统提供给我们的内置方法,每个函数都可以使用这三种方法,是因为apply、call和bind都实现在了Function的原型上(Function.prototype),而他们的作用都是给我们函数调用时显式绑定上this。下面先介绍一下它们的基本用法:

1.1 call 方法

call方法:使用一个指定的 this值和单独给出的一个或多个参数来调用一个函数。

  • 使用语法:func.call(thisArg, arg1, arg2, …)
    • thisArg:在func函数调用时绑定的this值;
    • arg1, arg2, …:指定的参数列表,将作为参数传递给func函数;
  • 使用效果:
function foo(x, y ,z) {
  console.log(this, x, y, z)
}

const obj = { name: 'curry', age: 30 }
/**
 * 1.将obj对象绑定给foo函数的this
 * 2.call剩余参数中的a b c分别传递给foo函数对应的三个参数
 */
foo.call(obj, 'a', 'b', 'c')

在这里插入图片描述

1.2 apply 方法

apply方法:调用一个具有给定this值的函数,以及以一个**数组(或类数组对象)**的形式提供的参数。

  • 使用语法:func.apply(thisArg, [argsArray])
    • thisArg:在func函数调用时绑定的this值;
    • [argsArray]:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给func函数;
  • 使用效果:
function foo(x, y ,z) {
  console.log(this, x, y, z)
}

const obj = { name: 'curry', age: 30 }
/**
 * 1.将obj对象绑定给foo函数的this
 * 2.数组中的1 2 3分别传递给foo函数对应的三个参数
 */
foo.apply(obj, [1, 2, 3])

在这里插入图片描述

1.3 bind

bind方法:创建一个新的函数,在bind()被调用时,这个新函数的this被指定为bind()的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

  • 使用语法:func.bind(thisArg[, arg1[, arg2[, …]]])
    • thisArg:调用func函数时作为this参数传递给目标函数的值;
    • arg1, arg2, …:当目标函数被调用时,被预置入func函数的参数列表中的参数;
  • 使用效果:
function foo(...args) {
  console.log(this, ...args)
}

const obj = { name: 'curry', age: 30 }
/**
 * 1.将obj对象绑定给foo函数的this
 * 2.bind剩余参数中的1 2 3分别传递给foo函数中参数
 * 3.也可在newFoo调用时传入参数,这时bind传递的参数会与newFoo调用时传递的参数进行合并
 */
const newFoo = foo.bind(obj, 1, 2, 3)
newFoo()
newFoo('a', 'b', 'c')

在这里插入图片描述

二、方法的实现

function foo(...arg) {
  console.log(this.name, ...arg);
}

function obj() {
  this.name = 'obj'
}

2.1 call 方法

Function.prototype.myCall = function(context, ...args) {
  // 1. 检查上下文是否为 null 或 undefined,如果是则设置为全局对象(浏览器中为 window)
  context = context || window;

  // 2. 为上下文对象创建一个唯一属性来保存当前函数,
  // 以便在调用完后删除这个属性,避免污染原始对象
  const fnKey = Symbol();
  context[fnKey] = this;

  // 3. 通过上下文对象调用函数并传入参数
  const result = context[fnKey](...args);

  // 4. 删除添加的属性
  delete context[fnKey];

  // 返回函数调用结果
  return result;
};

// 原始call方法
foo.call(obj, 1, 2, 3)
// 手写call方法
foo.myCall(obj, 1, 2, 3)

输出结果:
在这里插入图片描述

2.2 apply 方法

Function.prototype.myApply = function (context, args) {
  // 1.检查上下文是否为 null 或 undefined,如果是则设置为全局对象(浏览器中为 window)
  context = context || window;

  // 2. 为上下文对象创建一个唯一属性来保存当前函数,
  // 以便在调用完后删除这个属性,避免污染原始对象
  const fnKey = Symbol();
  context[fnKey] = this;

  let result;
  // 3. 检查是否传入了参数
  if (args) {
    // 使用扩展运算符展开参数数组,并通过上下文对象调用函数
    result = context[fnKey](...args);
  } else {
    // 如果未传入参数则直接通过上下文对象调用函数
    result = context[fnKey]();
  }

  // 4. 删除添加的属性
  delete context[fnKey];

  // 返回函数调用结果
  return result;
};

foo.apply(obj, [1, 2, 3])
foo.myApply(obj, [1, 2, 3])

输出结果:
在这里插入图片描述

2.3 bind 方法

Function.prototype.myBind = function(context, ...args) {
  const fn = this;

  return function(...innerArgs) {
    // 使用 apply 方法将传入的上下文和参数传递给函数,并返回调用结果
    return fn.apply(context, [...args, ...innerArgs]);
  };
};
let fn = foo.bind(obj,1,2,3)
let fn2 = foo.myBind(obj,1,2,3)
fn('a','b','c')
fn2('a','b','c')

输出结果:
在这里插入图片描述

更多推荐

【TypeScript】项目中对于TypeScript的打包处理

webpack通常情况下,实际开发中我们都需要使用构建工具对代码进行打包,TS同样也可以结合构建工具一起使用,下边以webpack为例介绍一下如何结合构建工具使用TS。步骤:初始化项目进入项目根目录,执行命令npminit-y主要作用:创建package.json文件下载构建工具npmi-Dwebpackwebpack

自动化项目实战:用requests库自动保存王者荣耀英雄皮肤到本地,文末附源码下载!

前言王者荣耀是一款备受欢迎的手机游戏,拥有众多精美的英雄皮肤。如果你想获取这些皮肤的图片或者其他相关信息,可以利用Python编写一个简单的爬虫来实现。安装第三方库首先,我们需要安装Python的requests和BeautifulSoup库。可以使用以下命令来安装它们:pipinstallrequestspipins

Ubuntu上通过源码方式安装Redis

上一篇文章Ubuntu上安装、使用Redis的详细教程已经介绍了再Ubuntu操作系统上安装Redis的详细过程,但是因为安装的Redis只有最主要的配置文件和redis-server,为了更深入地学习Redis和进行更复杂的操作,需要安装一个完整的Redis服务。这篇文章就介绍一下怎么在ubuntu上通过源码编译方式

[每周一更]-(第63期):Linux-nsenter命令使用说明

nsenter命令是一个可以在指定进程的命令空间下运行指定程序的命令。它位于util-linux包中。1、用途一个最典型的用途就是进入容器的网络命令空间。相当多的容器为了轻量级,是不包含较为基础的命令的,比如说ipaddress,ping,telnet,ss,tcpdump等等命令,这就给调试容器网络带来相当大的困扰:

HTTP 响应头Cache-Control

每个资源都可以通过Http头Cache-Control来定义自己的缓存策略,Cache-Control控制谁在什么条件下可以缓存响应以及可以缓存多久。最快的请求是不必与服务器进行通信的请求:通过响应的本地副本,我们可以避免所有的网络延迟以及数据传输的数据成本。为此,HTTP规范允许服务器返回一系列不同的Cache-Co

【Python】PySpark 数据计算 ④ ( RDD#filter 方法 - 过滤 RDD 中的元素 | RDD#distinct 方法 - 对 RDD 中的元素去重 )

文章目录一、RDD#filter方法1、RDD#filter方法简介2、RDD#filter函数语法3、代码示例-RDD#filter方法示例二、RDD#distinct方法1、RDD#distinct方法简介2、代码示例-RDD#distinct方法示例一、RDD#filter方法1、RDD#filter方法简介RD

MySQL-MHA

1、什么是MHAMHA(MasterHighAvailability)是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。MHA的出现就是解决MySQL单点的问题。MySQL故障切换过程中,MHA能做到0-30秒内自动完成故障切换操作。MHA能在故障切换的过程中最大程度上保证数据的一致性,以达到真正意义上的高可

UVA-1343 旋转游戏 题解答案代码 算法竞赛入门经典第二版

GitHub-jzplp/aoapc-UVA-Answer:算法竞赛入门经典例题和习题答案刘汝佳第二版题目其实不难,但是耗费了我较多时间。这种题关键就是在于找到约束条件,我在DFS的基础上,试了很多种策略:1.对3种数字,每种数字递归遍历一次,这样每次只需要关注一种数字的变化,情况更少。2.使用一个longlong类型

如何自动获取短信验证码?

点击下方关注我,然后右上角点击...“设为星标”,就能第一时间收到更新推送啦~~~这篇文章通过解决实际项目开发中遇到的如何自动获取短信验证码的问题,进一步讲述在Java中如何使用正则。Java中如何使用正则Java中正则相关类位于java.util.regex包下,主要使用2个类,如下:Pattern类:Pattern

tokio::net学习

tokio::net该模块包含TCP/UDP/Unix网络类型,类似于标准库,可用于实现网络协议。networkingprotocolsOrganizationTcpListenerandTcpStreamprovidefunctionalityforcommunicationoverTCPUdpSocketprovi

自己实现 SpringMVC 底层机制 系列之-实现任务阶段 7- 完成简单视图解析

😀前言自己实现SpringMVC底层机制系列之-实现任务阶段7-完成简单视图解析🏠个人主页:尘觉主页🧑个人简介:大家好,我是尘觉,希望我的文章可以帮助到大家,您的满意是我的动力😉😉在csdn获奖荣誉:🏆csdn城市之星2名⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣⁣💓Java全栈群星计

热文推荐