引入

在 JavaScript 中,函数里的 this 不一定固定指向某个对象,它和函数的调用方式有关。

callapply 的作用,就是在调用函数时,手动指定函数内部的 this 指向。

正文

定义

定义

callapply 都是函数对象上的方法,用来立即调用函数,并指定函数执行时内部的 this

简单说:

  • call:指定 this,参数一个一个传
  • apply:指定 this,参数用数组传

为什么需要它们

有时候一个函数不属于某个对象,但我们希望它在执行时,把 this 当成某个对象来用。

这时就可以使用 callapply

function sayName() {
  console.log(this.name);
}
 
const user = {
  name: "Tom",
};
 
sayName.call(user); // Tom

这里 sayName 本身不是 user 的方法,但通过 call(user),函数内部的 this 就指向了 user

call 语法

函数.call(thisArg, 参数1, 参数2, ...);
  • thisArg:函数内部 this 要指向的对象
  • 后面的参数:传给函数的普通参数
  • 立即执行函数

apply 语法

函数.apply(thisArg, [参数1, 参数2, ...]);
  • 第一个参数也是指定 this
  • 第二个参数必须是一个数组或类数组对象
  • 也会立即执行函数

例子

使用 call 改变 this

function introduce(age, city) {
  console.log(`我叫${this.name},今年${age}岁,来自${city}`);
}
 
const person = {
  name: "小明",
};
 
introduce.call(person, 18, "北京");

执行结果:

我叫小明,今年18岁,来自北京

这里 call 做了两件事:

  • introduce 函数里的 this 指向 person
  • 18"北京" 作为参数传进去

使用 apply 改变 this

function introduce(age, city) {
  console.log(`我叫${this.name},今年${age}岁,来自${city}`);
}
 
const person = {
  name: "小红",
};
 
introduce.apply(person, [20, "上海"]);

执行结果:

我叫小红,今年20岁,来自上海

applycall 的效果一样,只是传参方式不同。

callapply 的区别

方法是否立即执行参数写法
call参数一个一个写
apply参数放在数组里
fn.call(obj, a, b);
fn.apply(obj, [a, b]);

可以简单记成:

  • call 更适合参数数量明确的情况
  • apply 更适合参数已经放在数组里的情况

常见用法

借用对象方法

const person1 = {
  name: "Tom",
  sayHi() {
    console.log(`你好,我是${this.name}`);
  },
};
 
const person2 = {
  name: "Jerry",
};
 
person1.sayHi.call(person2);

执行结果:

你好,我是Jerry

这里 person2 没有 sayHi 方法,但可以临时借用 person1 的方法。

配合数组参数使用 apply

const nums = [3, 8, 2, 10];
 
const max = Math.max.apply(null, nums);
 
console.log(max); // 10

Math.max 原本需要一个一个传参数:

Math.max(3, 8, 2, 10);

如果参数已经在数组里,就可以用 apply 展开传入。

不过现在更常见的写法是使用展开运算符:

const max = Math.max(...nums);

注意事项

  • callapply 都会立即调用函数
  • 它们的第一个参数都是用来指定 this
  • call 后面的参数是逐个传入
  • apply 的第二个参数是数组形式
  • 如果函数内部没有用到 this,指定 this 通常没有意义
  • 箭头函数没有自己的 this,所以用 callapply 不能改变箭头函数的 this

箭头函数中的情况

const obj = {
  name: "Tom",
};
 
const fn = () => {
  console.log(this.name);
};
 
fn.call(obj);

箭头函数的 this 来自它定义时的外层作用域,不会因为 callapply 改变。

所以如果需要灵活改变 this,一般不要用箭头函数。

理解

可以把 callapply 理解为:临时换一个对象来执行这个函数

它们不是给对象永久添加方法,而是让函数在这一次执行时,把 this 当成指定的对象。

callapply 的核心区别不在功能,而在传参方式

  • 参数散着写,用 call
  • 参数已经在数组里,用 apply

引出

理解了 callapply 之后,就可以继续学习 bind,它也能改变 this,但不会立即执行函数。