apply、call、bind 的底层逻辑

250 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情。 先来了解一下这三个方法的基本情况,call、apply 和 bind 是挂在 Function 对象上的三个方法,调用这三个方法的必须是一个函数。

请看这三个函数的基本语法。

func.call(thisArg, param1, param2, ...)
func.apply(thisArg, [param1,param2,...])
func.bind(thisArg, param1, param2, ...)

其中 func 是要调用的函数,thisArg 一般为 this 所指向的对象,后面的 param1、2 为函数 func 的多个参数,如果 func 不需要参数,则后面的 param1、2 可以不写。

这三个方法共有的、比较明显的作用就是,都可以改变函数 func 的 this 指向。call 和 apply 的区别在于,传参的写法不同:apply 的第 2 个参数为数组; call 则是从第 2 个至第 N 个都是给 func 的传参;而 bind 和这两个(call、apply)又不同,bind 虽然改变了 func 的 this 指向,但不是马上执行,而这两个(call、apply)是在改变了函数的 this 指向之后立马执行。

let a = {
  name: 'jack',
  getName: function(msg) {
    return msg + this.name;
  } 
}
let b = {
  name: 'lily'
}
console.log(a.getName('hello~'));  // hello~jack
console.log(a.getName.call(b, 'hi~'));  // hi~lily
console.log(a.getName.apply(b, ['hi~']))  // hi~lily
let name = a.getName.bind(b, 'hello~');
console.log(name());  // hello~lily

从上面的代码执行的结果中可以发现,使用这三种方式都可以达成我们想要的目标,即通过改变 this 的指向,让 b 对象可以直接使用 a 对象中的 getName 方法。从结果中可以看到,最后三个方法输出的都是和 lily 相关的打印结果,满足了我们的预期。

关于这三个方法的原理相关先介绍到这里,我们再看看这几个方法的使用场景。

判断数据类型

用 Object.prototype.toString 来判断类型是最合适的

function getType(obj){
  let type  = typeof obj;
  if (type !== "object") {
    return type;
  }
  return Object.prototype.toString.call(obj).replace(/^$/, '$1');
}

类数组借用方法

var arrayLike = { 
  0: 'java',
  1: 'script',
  length: 2
} 
Array.prototype.push.call(arrayLike, 'jack', 'lily'); 
console.log(typeof arrayLike); // 'object'
console.log(arrayLike);
// {0: "java", 1: "script", 2: "jack", 3: "lily", length: 4}

继承

我们在上一讲中说到了继承,它与 new、call 共同实现了各种各样的继承方式。那么下面我们结合着这一讲的内容再来回顾一下组合继承方式,代码如下。

  function Parent3 () {
    this.name = 'parent3';
    this.play = [1, 2, 3];
  }
  Parent3.prototype.getName = function () {
    return this.name;
  }
  function Child3() {
    Parent3.call(this);
    this.type = 'child3';
  }
  Child3.prototype = new Parent3();
  Child3.prototype.constructor = Child3;
  var s3 = new Child3();
  console.log(s3.getName());  // 'parent3'