返回

手撕call和apply函数,详细解析

前端

前言

call()和apply()可以说是比较常用的方法了,今天就让我们来聊聊手写call和apply函数,在开始前我我们先来有关于这俩函数的一些内容。

作用与区别

call()和apply()是JavaScript中用来改变函数调用时this对象指向的函数。这两个函数的作用是将一个函数的this对象指向另一个对象,从而可以改变函数内部this的指向。

call() 的用法是:func.call(thisArg, arg1, arg2, ...),其中func是需要调用的函数,thisArg是要将this指向的对象,arg1、arg2、...是要传递给函数的参数。

apply() 的用法是:func.apply(thisArg, [args]),其中func是需要调用的函数,thisArg是要将this指向的对象,args是要传递给函数的参数,args是一个数组。

call()和apply()的区别在于:

  • call()接受多个参数,而apply()只接受一个参数,该参数是一个数组。
  • call()中的参数是逐个传递的,而apply()中的参数是作为一个数组传递的。

实现原理

现在我们就来手撕实现call和apply函数。

// 定义call函数
Function.prototype.myCall = function (context) {
  // 获取调用函数的参数
  let args = [...arguments].slice(1);
  // 将调用函数的this指向context
  context.fn = this;
  // 调用函数并返回结果
  let result = context.fn(...args);
  // 删除context上的fn属性
  delete context.fn;
  return result;
};

// 定义apply函数
Function.prototype.myApply = function (context, args) {
  // 将调用函数的this指向context
  context.fn = this;
  // 调用函数并返回结果
  let result = context.fn(...args);
  // 删除context上的fn属性
  delete context.fn;
  return result;
};

示例

下面我们通过一个示例来看看如何使用call()和apply()函数。

// 定义一个函数
function sayName(name) {
  console.log(this.name + ' ' + name);
}

// 创建一个对象
const person = {
  name: '张三'
};

// 使用call()函数调用sayName函数
sayName.myCall(person, '李四'); // 张三 李四

// 使用apply()函数调用sayName函数
sayName.myApply(person, ['李四']); // 张三 李四

在上面的示例中,我们首先定义了一个名为sayName的函数,该函数接受一个参数name并输出“this.name name”。然后,我们创建了一个名为person的对象,该对象的name属性值为“张三”。接下来,我们使用call()和apply()函数调用sayName函数,并将person对象作为this对象。最后,我们在控制台中输出结果。

扩展应用

call()和apply()函数可以用来扩展和增强函数的功能。例如,我们可以使用call()和apply()函数来实现函数柯里化、函数借用等操作。

函数柯里化

函数柯里化是指将一个函数拆分成多个小函数,其中每个小函数都接受一个参数,并且返回一个新的函数。这样,我们可以通过多次调用小函数来逐步构建最终的函数。

// 定义一个函数
function sum(a, b, c) {
  return a + b + c;
}

// 使用call()函数实现函数柯里化
const currySum = sum.myCall(null, 1);
const result = currySum(2)(3); // 6

在上面的示例中,我们首先定义了一个名为sum的函数,该函数接受三个参数a、b和c并返回它们的和。然后,我们使用call()函数将sum函数的this指向null,并将其第一个参数设置为1。这样,我们就得到了一个新的函数currySum,该函数接受两个参数b和c,并返回它们的和加上1。最后,我们调用currySum函数两次,分别传入参数2和3,得到最终结果6。

函数借用

函数借用是指将一个函数的属性或方法借用给另一个函数。这样,我们可以使用另一个函数来调用借用函数的属性或方法。

// 定义一个对象
const person = {
  name: '张三',
  sayName: function () {
    console.log(this.name);
  }
};

// 使用apply()函数实现函数借用
const sayName = person.sayName.myApply(null);
sayName(); // 张三

在上面的示例中,我们首先定义了一个名为person的对象,该对象的name属性值为“张三”,并有一个名为sayName的方法。然后,我们使用apply()函数将person.sayName方法的this指向null,并将其调用。这样,我们就借用了person对象的sayName方法,并将其用作一个独立的函数。最后,我们调用sayName函数,输出结果“张三”。

总结

call()和apply()函数是JavaScript中非常有用的函数,它们可以用来改变函数调用时this对象指向,从而扩展和增强函数的功能。本文对call()和apply()函数进行了详细的讲解,包括它们的用法、实现原理、示例以及扩展应用。希望读者能够通过本文对call()和apply()函数有更深入的理解,并能够熟练应用它们来优化和简化代码。