03 - js的 call、apply、bind

96 阅读4分钟

注意:不要使用 call 、apply 链式调用构造函数(例如:实现继承)。这会将构造函数作为普通函数调用,这意味着 new.target的值为undefined,而类会抛出错误,因为它们不能在没有new的情况下被调用

Function.prototype.call()

  1. Function实例的 call() 方法会以给定的 this值 和 逐个提供的参数调用该函数

  2. 语法格式:

call (thisArg, arg1, arg2, /* …, */ argN) 
  • thisArg:在调用func时要使用的 this值,如果函数不在严格模式下,null和undefined将被替换成全局对象,并且原始值将被转换为对象
  • arg1 ... argN(可选):函数的参数
  1. 返回值:

    1.   使用指定的 this值 和参数 调用后的结果
    2. function greet() {
        console.log(this.animal, "的睡眠时间一般在", this.sleepDuration, "之间");
      }
      
      const obj = {
        animal: "猫",
        sleepDuration: "12 到 16 小时",
      };
      
      greet.call(obj); // 猫 的睡眠时间一般在 12 到 16 小时 之间
      

应用场景

  1. 对象的继承

    1. function superClass () {
          this.a = 1;
          this.print = function () {
              console.log(this.a);
          }
      }
      
      function subClass () {
          superClass.call(this);
          this.print();
      }
      
      subClass(); // 1
      
  2. 借用方法

    1. let domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
      // 这样子domNode就可以应用数组下的所有方法
      

Function.prototype.apply()

  1. Function实例的 apply() 方法会以给定的 this值 和 作为数组(或类数组对象)提供的 arguments 调用该函数

  2. 语法格式

    1. apply(thisArg, argsArray) 
      
    2. thisArg:在调用func时要使用的 this值,如果函数不在严格模式下,null和undefined将被替换成全局对象,并且原始值将被转换为对象
    3. argsArray(可选):一个类数组对象,用于指定调用func时的参数,或者如果不需要向函数提供参数,则为null或undefined
  3. 返回值

使用指定的 this值 和 参数 调用函数的结果

  • 将数组各项加到另一个数组 (可以使用concat方法,但是它不会将元素追加到已有数组中,而是创建并返回一个新数组)
const array = ["a", "b"];
const elements = [0, 1, 2];
// 使用 push
array.push(...elements);

// 使用 apply 隐式地将一个数组作为一系列参数展开
array.push.apply(array, elements);
console.info(array); // ["a", "b", 0, 1, 2]
  • 第二个参数会被转换成类数组

类数组:具备与数组特征类似的对象。类数组可以通过角标[]进行调用,具备length属性,也可以使用for循环遍历,但是无法使用forEach、splice、push等数组的原生方法

func.apply(obj, [1,2,3])
// func 接收到的参数实际上是 1,2,3

func.apply(obj, {
    0: 1,
    1: 2,
    2: 3,
    length: 3
})   // 这个对象就是一个类数组
// func 接收到的参数实际上是 1,2,3

apply函数和call函数几乎完全相同,只是函数参数在call中逐个作为列表传递,而在apply中它们会组合在一个对象中,通常是一个数组。例如,func.call(this, "eat", "bananas")func.apply(this, ["eat", "bananas"])

应用场景

  1. Math.max / min

    1. let max = Math.max.apply(null, array);
      
  2. 实现两个数组合并,以上例子中

Function.prototype.bind()

  1. Function 实例的 bind() 方法创建一个新函数,当调用该新函数时,它会调用原始函数并将其 this 关键字设置为给定的值,同时,还可以传入一系列指定的参数,这些参数会插入到调用新函数时传入的参数的前面

  2. 语法格式

    1. bind(thisArg, arg1, arg2, /* …, */ argN) 
      
    2. thisArg:在调用绑定函数时,作为 this 参数传入目标函数 func 的值。如果函数不在严格模式下,null 和 undefined 会被替换为全局对象,并且原始值会被转换为对象。如果使用 new 运算符构造绑定函数,则忽略该值
    3. argsArray(可选):在调用 func时,插入到传入绑定函数的参数前的参数
  3. 返回值:

使用指定的 this 值和初始参数(如果提供)创建的给定函数的副本

const module = {
  x: 42,
  getX: function () {
    return this.x;
  },
};

const unboundGetX = module.getX;
console.log(unboundGetX());  // undefined 此时隐式绑定丢失,this指向window 但是window不存在属性x 所以返回undefined

const boundGetX = unboundGetX.bind(module);
console.log(boundGetX());   // 42

总结

  1. call、apply主要是改变对象的执行上下文,并且是立即执行,传参的方式略有不同
  2. bind也能改变对象的执行上下文,但是它返回的是一个函数,需要再调用才会执行

参考文章

「干货」细说 call、apply 以及 bind 的区别和用法 - 掘金

link.juejin.cn/?target=htt…