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

目录
  • 前言
  • 1. call
    • 1.1 语法
    • 1.2 流程图
    • 1.3 代码实现
  • 2. apply
    • 2.1 语法
    • 2.2 流程图
    • 2.3 代码实现
  • 3. bind
    • 3.1 语法
    • 3.2 流程图
    • 3.3 代码实现
  • 4.全文总结
    • 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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解JS中的this、apply、call、bind(经典面试题)

    这又是一个面试经典问题~/(ㄒoㄒ)/~~也是 ES5中众多坑中的一个,在 ES6 中可能会极大避免 this 产生的错误,但是为了一些老代码的维护,最好还是了解一下 this 的指向和 call.apply.bind 三者的区别. this 的指向 在 ES5 中,其实 this 的指向,始终坚持一个原理:this 永远指向最后调用它的那个对象,来,跟着我朗读三遍:this 永远指向最后调用它的那个对象,this 永远指向最后调用它的那个对象,this 永远指向最后调用它的那个对象.记住这句话

  • JS中call(),apply(),bind()函数的区别与用法详解

    call() 介绍 通过提供一个新的this值给当前调用的函数/方法,从而改变this指向. 语法 fn.call(this.Arg, arg1, arg2,...) thisArg:当前调用函数this指向的对象arg1, arg2:传递的其他参数(直接传给形参可不写) 特点 可以直接调用函数—fn.call() 可以改变被调用函数的this指向为指定的— fn.call(this.Arg) 返回值 使用调用者提供的值和参数调用该函数的返回值,也就是函数的返回值.若该方法没有返回值,则返回un

  • 浅谈JavaScript中的apply/call/bind和this的使用

    fun.apply(context,[argsArray]) 立即调用fun,同时将fun函数原来的this指向传入的新context对象,实现同一个方法在不同对象上重复使用. context:传入的对象,替代fun函数原来的this: argsArray:一个数组或者类数组对象,其中的数组参数会被展开作为单独的实参传给 fun 函数,需要注意参数的顺序. fun.call(context,[arg1],[arg2],[-]) 同apply,只是参数列表不同,call的参数需要分开一个一个传入.

  • JS 函数的 call、apply 及 bind 超详细方法

    目录 JS 函数的 call.apply 及 bind 方法 一.call() 方法 1.call()方法的模拟实现 二.apply() 方法 1.apply()方法的模拟实现 三.bind() 方法 1.bind() 方法的模拟实现 四.总结 JS 函数的 call.apply 及 bind 方法 一.call() 方法 调用 call() 方法会立即执行目标函数,同时改变函数内部 this 的指向.this 指向由方法的第一个参数决定,后面逐个列举的任意个参数将作为目标函数的参数一一对应传入

  • JavaScript函数之call、apply以及bind方法案例详解

    总结 1.相同点 都能够改变目标函数执行时内部 this 的指向 方法的第一个参数用于指定函数执行时内部的 this 值 支持向目标函数传递任意个参数 若不向方法的第一个参数传值或者传递 undefined.null,则在 JavaScript 正常模式下,目标函数内部的 this 指向 window 对象,严格模式下,分别指向 undefined.null. 2.区别 apply() 方法可接收两个参数,而 call() 和 bind() 方法则可接收多个参数. apply() 方法向目标函数

  • 一文搞懂JavaScript中bind,apply,call的实现

    目录 bind.call和apply的用法 bind call&apply 实现bind 实现call和apply 总结 bind.call和apply都是Function原型链上面的方法,因此不管是使用function声明的函数,还是箭头函数都可以直接调用.这三个函数在使用时都可以改变this指向,本文就带你看看如何实现bind.call和apply. bind.call和apply的用法 bind bind()方法可以被函数对象调用,并返回一个新创建的函数. 语法: function.bin

  • JavaScript手写call,apply,bind方法

    目录 前言 改写this实现思路 前期准备 手写call方法 手写apply方法 手写bind方法 前言 改变this指向在书写业务的时候经常遇到,我们经常采用以下方法进行改写 使用作用声明变量存储this 使用jJavaScript的原生方法call,apply,以及bind进行改写 第一种方法就不说了,就是一个变量存储的问题,主要说第二种如何实现的 call,bind,apply方法都是JavaScript原生的方法,挂载在Function原型上,使得所有函数都可以调用,今天我们来实现一下c

  • javascript中call,apply,bind函数用法示例

    本文实例讲述了javascript中call,apply,bind函数用法.分享给大家供大家参考,具体如下: 一.call函数 a.call(b); 简单的理解:把a对象的方法应用到b对象上(a里如果有this,会指向b) call()的用法:用在函数上面 var Dog=function(){ this.name="汪星人"; this.shout=function(){ alert(this.name); } }; var Cat=function(){ this.name=&qu

  • javascript中call,apply,bind的区别详解

    在JS中,这三者都是用来改变函数的this对象的指向的,他们有什么样的区别呢. 在说区别之前还是先总结一下三者的相似之处: 1.都是用来改变函数的this对象的指向的. 2.第一个参数都是this要指向的对象. 3.都可以利用后续参数传参. 那么他们的区别在哪里的,先看一个例子. var xw = { name : "小王", gender : "男", age : 24, say : function() { alert(this.name + " ,

  • javascript中call,apply,bind的用法对比分析

    关于call,apply,bind这三个函数的用法,是学习javascript这门语言无法越过的知识点.下边我就来好好总结一下它们三者各自的用法,及常见的应用场景. 首先看call这个函数,可以理解成"借用","请求".想像一下如下的情景:你孤单一人漂泊在外,有急事想往家里打电话,可是很不巧,手机欠费了,或者没电了,或者掉坑里了,总之你的手机就是用不成.可是你非打这个电话不可,于是你可以去借一下朋友的手机,或者借用一下邻居的手机,或者公用电话,这样呢,你就可以在自己

  • Javascript中的this,bind和that使用实例

    这篇文章主要介绍了Javascript中的this,bind和that使用实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Javascript中必须通过this来访问类成员,可是this的特点就是函数绑在哪个对象上,它就指向那个对象.这个可能困扰过很多的程序员,特别是从C#,Java等语言过来的程序员. function Foo(){ this.message = 'This is message from Foo'; } Foo.prot

  • 彻底搞懂JavaScript中的apply和call方法(必看)

    call和apply都是为了改变某个函数运行的context上下文而存在的,即为了改变函数体内部this的指向.因为JavaScript的函数存在定义上下文和运行时上下文以及上下文是可以改变的概念. 回到目录定义 fun.apply(thisArg, [argsArray]) fun.call(thisArg, arg1,arg2, ...) 其中thisArg可以为null或undefined,此时表示全局对象,更详细见MDN:apply().call() 二者的作用完全一样,只是接受参数的方

  • javascript中call,apply,callee,caller用法实例分析

    本文实例讲述了javascript中call,apply,callee,caller用法.分享给大家供大家参考,具体如下: 实践一:call,apply 用来让一个对象去调用本不属于自己的方法,两者都可以传递参数,call的参数是列表形式,apply的参数是数组形式 var person = { "name":"Tom", "say":function(){ console.log("person say"); }, &quo

  • 一文剖析JavaScript中闭包的难点

    目录 一.作用域基本介绍 1. 全局作用域 2. 函数作用域 3. 块级作用域 二.什么是闭包 1. 闭包的基本概念 2. 闭包产生的原因 3. 闭包的表现形式 三.如何解决循环输出问题 1. 利用 IIFE 2. 使用 ES6 中的 let 3. 定时器传入第三个参数 一.作用域基本介绍 ES6之前只有全局作用域与函数作用域两种,ES6出现之后,新增了块级作用域. 1. 全局作用域 在JavaScript中,全局变量是挂载在window对象下的变量,所以在网页中的任何位置你都可以使用并且访问到

  • javascript中call apply 与 bind方法详解

    在JavaScript中,call.apply和bind是Function对象自带的三个方法,本文将通过几个场景的应用,来详细理解三个方法. call() call() 方法在使用一个指定的this值和若干个指定的参数值的前提下调用某个函数或方法. 当调用一个函数时,可以赋值一个不同的 this 对象.this 引用当前对象,即 call 方法的第一个参数. 通过 call 方法,你可以在一个对象上借用另一个对象上的方法,比如Object.prototype.toString.call([]),

  • 跟我学习javascript的call(),apply(),bind()与回调

    一.call(),apply(),bind()方法 JavaScript 中通过call或者apply用来代替另一个对象调用一个方法,将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象.简单的说就是改变函数执行的上下文,这是最基本的用法.两个方法基本区别在于传参不同. call(obj,arg1,arg2,arg3); call第一个参数传对象,可以是null.参数以逗号分开进行传值,参数可以是任何类型. apply(obj,[arg1,arg2,arg3]); appl

随机推荐