一文了解JavaScript中call/apply/bind的使用

 更新时间:2022年07月25日 08:53:46   作者:​ 橘皮​  
这篇文章主要介绍了一文了解JavaScript中call/apply/bind的使用,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下

前言

在JavaScript中,经常会通过call / apply / bind 函数来改变this的指向,详情可看一文带你了解this指向,今天我们来研究下这三个函数的实现。

1. call

call()函数是什么?

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。也就是说call() 改变了this指向并执行了函数

1.1 语法

func.call(thisArg, arg1, arg2, ...)
// thisArg为在 func 函数运行时使用的 this 值
// arg1, arg2等为指定的参数列表
// 其返回值为调用有指定 this 值和参数的函数的结果

1.2 流程图

一般来说,我们要模拟实现call,可以分为以下几个步骤:

  • 将函数设置为对象的属性, 当对象为null或undefined, 设为window对象
  • 取出函数执行所需参数,执行该函数
  • 如果函数存在返回值,在返回后删除该函数

以下就是call()方法实现的流程图:

1.3 代码实现

Function.prototype.call = function (thisArg, ...argsArray) {
  if (typeof this !== "function") {
    throw new TypeError(
      "Function.prototype.call was called on which is not a function"
    );
  }

  if (thisArg === undefined || thisArg === null) {
    thisArg = window;
  } else {
    thisArg = Object(thisArg);
  }

  // 将 func 放入 thisArg 内,这样在调用 thisArg[func] 时 this 自然就指向了 thisArg
  const func = Symbol("func");
  thisArg[func] = this;

  let result;

  if (argsArray.length) {
    result = thisArg[func](...argsArray);
  } else {
    result = thisArg[func]();
  }

  delete thisArg[func];

  return result;
};

2. apply

apply()函数是什么?

apply() 方法调用一个具有给定 this 值的函数,以及以一个数组(或一个类数组对象)的形式提供的参数。同call()的功能,改变this指向的同时执行了函数。

2.1 语法

func.apply(thisArg, [argsArray]);
// thisArg为在 func 函数运行时使用的 this 值
// arg1, arg2等为指定的参数列表
// 其返回值为调用有指定 this 值和参数的函数的结果

2.2 流程图

apply()方法实现的流程基本与call的实现流程没有太多差异,只需要对函数参数数组进行判断展开即可。

以下是apply()函数的流程图:

2.3 代码实现

Function.prototype.apply = function (thisArg, argsArray) {
  if (typeof this !== "function") {
    throw new TypeError(
      "Function.prototype.apply was called on which is not a function"
    );
  }

  if (thisArg === undefined || thisArg === null) {
    thisArg = window;
  } else {
    thisArg = Object(thisArg);
  }

  // 将 func 放入 thisArg 内,这样在调用 thisArg[func] 时 this 自然就指向了 thisArg
  const func = Symbol("func");
  thisArg[func] = this;

  let result;

  if (argsArray && typeof argsArray === "object" && "length" in argsArray) {
    // 此处使用 Array.from 包裹让其支持形如 { length: 1, 0: 1 } 这样的类数组对象,直接对 argsArray 展开将会执行出错
    result = thisArg[func](...Array.from(argsArray));
  } else if (argsArray === undefined || argsArray === null) {
    result = thisArg[func]();
  } else {
    throw new TypeError("CreateListFromArrayLike called on non-object");
  }
  delete thisArg[func];
  return result;
};

3. bind

💡bind() 函数是什么?

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

3.1 语法

func.bind(thisArg[, arg1[, arg2[, ...]]])
// thisArg 为调用绑定函数时作为 this 参数传递给目标函数的值, 如果使用 new 运算符构造绑定函数,忽略该值
// arg1, arg2为当目标函数被调用时,被预置入绑定函数的参数列表中的参数。
// 其返回值为一个原函数的拷贝,并拥有指定的 this 值和初始参数

3.2 流程图

想要实现bind函数,即需实现两个特点:一为返回一个函数;二为可以传入参数。

所以我们可从以下几点入手:

  • 通过使用`call`或者`apply`实现 `this`的指定;
  • 实现在`bind`的时候可以传参,在执行返回函数时传参;
  • 判断是否使用 `new`操作符来确定`this`指向。

话不多说,下面就是bind函数的流程图:

3.3 代码实现

Function.prototype.bind = function (thisArg, ...argsArray) {
  if (typeof this !== "function") {
    throw new TypeError(
      "Function.prototype.bind was called on which is not a function"
    );
  }
  if (thisArg === undefined || thisArg === null) {
    thisArg = window;
  } else {
    thisArg = Object(thisArg);
  }
  const func = this;
  const bound = function (...boundArgsArray) {
    let isNew = false;

    // 如果 func 不是构造器,直接使用 instanceof 将出错,所以需要用 try...catch 包裹
    try {
      isNew = this instanceof func;
    } catch (error) {}

    return func.apply(isNew ? this : thisArg, argsArray.concat(boundArgsArray));
  };

  const Empty = function () {};
  Empty.prototype = this.prototype;
  bound.prototype = new Empty();

  return bound;
};

4.全文总结

call、apply与bind有什么区别?

  • calll、apply 与 bind 都用于this绑定,但 call、apply 函数在改变this指向的同时还会执行函数;而 bind 函数在改变this后返回一个全新的绑定函数。
  • bind 属于硬绑定,返回的绑定函数的this指向不能再通过 bind、apply 或 call 修改,即this被永久绑定;call 与 apply 只适用于当前调用,一次调用后就结束。
  • call 和 apply 功能完全相同,但call 从第二个参数后的所有参数都是原函数的参数;而 apply 只接受两个参数,第二个参数必须是数组,该数组包含着原函数的参数列表。

到此这篇关于一文了解JavaScript中call/apply/bind的使用的文章就介绍到这了,更多相关JavaScript call,apply,bind内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JS创建对象的写法示例

    JS创建对象的写法示例

    这篇文章主要介绍了JS创建对象的写法,结合实例形式分析了javascript对象的定义、实现方法与使用技巧,需要的朋友可以参考下
    2016-11-11
  • javascript轮播图算法

    javascript轮播图算法

    这篇文章主要为大家详细介绍了javascript轮播图算法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • js获取当前日期昨天、今天、明天日期的不同方法总结

    js获取当前日期昨天、今天、明天日期的不同方法总结

    JS中处理日期时间常用Date对象,下面这篇文章主要给大家介绍了关于利用js获取当前日期昨天、今天、明天日期的不同方法,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-11-11
  • javascript阻止scroll事件多次执行的思路及实现

    javascript阻止scroll事件多次执行的思路及实现

    阻止scroll事件多次执行主要是为了解决一些常见网页特效在js解析时预期与效果不同,感兴趣的朋友可以了解下
    2013-11-11
  • js获取当前周、上一周、下一周日期

    js获取当前周、上一周、下一周日期

    本文主要介绍了js获取当前周、上一周、下一周日期的实例,具有很好的参考价值。下面跟着小编一起来看下吧
    2017-03-03
  • 理解javascript中的with关键字

    理解javascript中的with关键字

    这篇文章主要帮助大家理解javascript中的with关键字,学习with关键字的作用,感兴趣的小伙伴们可以参考一下
    2016-02-02
  • JS判断浏览器是否安装flash插件的简单方法

    JS判断浏览器是否安装flash插件的简单方法

    下面小编就为大家带来一篇JS判断浏览器是否安装flash插件的简单方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-09-09
  • 基于JavaScript实现的希尔排序算法分析

    基于JavaScript实现的希尔排序算法分析

    这篇文章主要介绍了基于JavaScript实现的希尔排序算法,简单分析了希尔排序的原理并结合实例形式给出了javascript实现希尔排序的操作步骤与相关注意事项,需要的朋友可以参考下
    2017-04-04
  • uni-app实现页面通信EventChannel的操作方法

    uni-app实现页面通信EventChannel的操作方法

    使用了EventBus的方法实现不同页面组件之间的一个通信,在uni-app中,我们也可以使用uni-app API,uni.navigateTo来实现页面间的通信,这篇文章主要介绍了uni-app实现页面通信EventChannel的操作方法,需要的朋友可以参考下
    2024-05-05
  • JS数据结构与算法中的队列结构详解

    JS数据结构与算法中的队列结构详解

    队列指的是一种受限的线性表,先进先出,今天通过本文带领大家认识队列及队列的应用,对JS数据结构与算法-队列结构相关知识感兴趣的朋友一起看看吧
    2022-11-11

最新评论