掌握JavaScript中call()和apply()的精髓,让你的函数调用更加灵活高效

2023-12-22 20:09:52 浏览数 (2)

前言

JavaScript 是一门非常强大的编程语言,它支持多种编程范式,包括面向对象编程。在 JavaScript 中,我们可以使用 call() 和 apply() 两个方法来调用函数并且改变函数的上下文。这两个方法在 JavaScript 中是非常常用的,但是很多新手对它们的理解还不够深入。在本文中,我们将详细介绍 call() 和 apply() 的区别与用法,帮助读者更好地理解它们。

正文内容

一、call() 和 apply() 的作用

在 JavaScript 中,函数是一等公民,我们可以像使用其他类型的变量一样使用函数。我们可以将函数赋值给变量,将函数作为参数传递给其他函数,或者将函数作为返回值返回给其他函数。但是,函数的上下文是不能像变量一样随意传递的。函数的上下文是指函数内部 this 关键字的指向。在 JavaScript 中,函数的上下文默认是全局对象,但是我们可以通过 call() 和 apply() 方法来改变函数的上下文。

call() 和 apply() 方法的作用都是调用一个函数,并且改变函数的上下文。它们的语法如下:

代码语言:javascript复制
function.call(thisArg, arg1, arg2, ...)
function.apply(thisArg, [argsArray])

复制

其中,thisArg 表示要改变的函数上下文,arg1、arg2、... 表示函数的参数列表。如果使用 apply() 方法,则参数列表需要以数组的形式传递。这两个方法的区别在于参数的传递方式不同。

二、call() 和 apply() 的区别

call() 和 apply() 的区别在于参数的传递方式不同。call() 方法的参数是一个一个传递的,而 apply() 方法的参数需要以数组的形式传递。下面我们来看一个例子:

代码语言:javascript复制
function sayHello(name, age) {
  console.log(`Hello, my name is ${name}, I'm ${age} years old.`);
}

sayHello.call(null, 'Tom', 18);
sayHello.apply(null, ['Tom', 18]);

复制

在这个例子中,我们定义了一个函数 sayHello,它接受两个参数 name 和 age。我们使用 call() 和 apply() 方法分别调用这个函数,并且传递相同的参数。在 call() 方法中,我们将参数一个一个传递,而在 apply() 方法中,我们将参数放在一个数组中传递。这两个方法的输出结果是相同的:

代码语言:javascript复制
Hello, my name is Tom, I'm 18 years old.

复制

除了参数的传递方式不同,call() 和 apply() 还有一些细微的差别。下面我们来详细介绍一下这些差别。

1. 参数传递方式不同

我们已经看到了,call() 和 apply() 方法的参数传递方式不同。这是两个方法最明显的区别。

2. 参数个数不同

在 JavaScript 中,函数的参数个数是可以变化的。我们可以定义一个函数,不传递任何参数,也可以定义一个函数,传递多个参数。在这种情况下,call() 和 apply() 方法的行为也是不同的。

当我们使用 call() 方法调用一个函数时,可以传递任意个数的参数。如果传递的参数个数不足,那么剩余的参数将会被忽略。如果传递的参数个数超过函数定义时的参数个数,那么多余的参数将会被忽略。

当我们使用 apply() 方法调用一个函数时,需要将参数放在一个数组中传递。如果数组中的元素个数不足,那么剩余的参数将会被设置为 undefined。如果数组中的元素个数超过函数定义时的参数个数,那么多余的元素将会被忽略。

下面我们来看一个例子:

代码语言:javascript复制
function sayHello(name, age) {
  console.log(`Hello, my name is ${name}, I'm ${age} years old.`);
}

sayHello.call(null, 'Tom', 18, 'male');
sayHello.apply(null, ['Tom', 18, 'male']);

复制

在这个例子中,我们定义了一个函数 sayHello,它接受两个参数 name 和 age。我们使用 call() 和 apply() 方法分别调用这个函数,并且传递了三个参数。在 call() 方法中,我们传递了三个参数,但是第三个参数被忽略了。在 apply() 方法中,我们将参数放在一个数组中传递,但是第三个元素被忽略了。这两个方法的输出结果是相同的:

代码语言:javascript复制
Hello, my name is Tom, I'm 18 years old.

复制

3. thisArg 参数的处理方式不同

在 JavaScript 中,thisArg 参数是用来指定函数的上下文的。在 call() 方法中,thisArg 参数可以为任意值,包括 null 和 undefined。在 apply() 方法中,thisArg 参数必须为对象,如果传递的不是对象,则会自动转换为对象。

下面我们来看一个例子:

代码语言:javascript复制
function sayHello(name, age) {
  console.log(`Hello, my name is ${name}, I'm ${age} years old. My gender is ${this.gender}.`);
}

var person = {
  gender: 'male'
};

sayHello.call(person, 'Tom', 18);
sayHello.apply(person, ['Tom', 18]);

复制

在这个例子中,我们定义了一个函数 sayHello,它接受两个参数 name 和 age,还使用了 this 关键字来引用 gender 属性。我们定义了一个对象 person,它有一个 gender 属性。我们使用 call() 和 apply() 方法分别调用这个函数,并且将 person 对象作为 thisArg 参数传递。在 call() 方法中,我们将 person 对象直接传递给了 thisArg 参数。在 apply() 方法中,我们将 person 对象放在一个数组中传递。这两个方法的输出结果是相同的:

代码语言:javascript复制
Hello, my name is Tom, I'm 18 years old. My gender is male.

复制

4. 性能不同

在 JavaScript 中,函数的调用是有一定的开销的。每次调用函数,都需要将函数压入调用栈,然后执行函数体,最后将函数弹出调用栈。在这个过程中,会产生一定的开销。在这种情况下,call() 和 apply() 方法的性能也是不同的。

在大多数情况下,使用 call() 方法调用函数的性能要比使用 apply() 方法调用函数的性能要好。这是因为 call() 方法只需要将参数一个一个传递,而 apply() 方法需要将参数放在一个数组中传递,这会产生一定的开销。但是,这种差别在实际应用中并不是很明显,只有在调用函数的次数非常多的情况下才会产生明显的影响。

三、call() 和 apply() 的用途

call() 和 apply() 方法的用途非常广泛,下面我们来看一些常见的用途。

1. 改变函数的上下文

call() 和 apply() 方法最常见的用途就是改变函数的上下文。在 JavaScript 中,函数的上下文默认是全局对象,但是我们可以使用 call() 和 apply() 方法来将函数的上下文改变为其他对象。这个特性非常有用,可以让我们在不改变函数原有代码的情况下,改变函数的行为。

下面我们来看一个例子:

代码语言:javascript复制
var person1 = {
  name: 'Tom',
  sayHello: function() {
    console.log(`Hello, my name is ${this.name}.`);
  }
};

var person2 = {
  name: 'Jerry'
};

person1.sayHello(); // 输出:Hello, my name is Tom.
person1.sayHello.call(person2); // 输出:Hello, my name is Jerry.

复制

在这个例子中,我们定义了一个对象 person1,它有一个 sayHello 方法,可以输出自己的名字。我们还定义了一个对象 person2,它也有一个 name 属性。我们使用 call() 方法调用 person1 的 sayHello 方法,并且将 person2 对象作为 thisArg 参数传递。这样,sayHello 方法的上下文就变成了 person2 对象,输出结果也相应地改变了。

2. 借用其他对象的方法

在 JavaScript 中,对象可以通过原型链继承其他对象的方法。但是,有时候我们需要借用其他对象的方法,而不是继承它们。这种情况下,call() 和 apply() 方法就可以派上用场了。

下面我们来看一个例子:

代码语言:javascript复制
var person = {
  name: 'Tom',
  sayHello: function() {
    console.log(`Hello, my name is ${this.name}.`);
  }
};

var dog = {
  name: 'Snoopy'
};

person.sayHello.call(dog); // 输出:Hello, my name is Snoopy.

复制

在这个例子中,我们定义了一个对象 person,它有一个 sayHello 方法,可以输出自己的名字。我们还定义了一个对象 dog,它也有一个 name 属性。我们使用 call() 方法调用 person 的 sayHello 方法,并且将 dog 对象作为 thisArg 参数传递。这样,sayHello 方法的上下文就变成了 dog 对象,输出结果也相应地改变了。

3. 调用函数并传递参数

除了改变函数的上下文之外,call() 和 apply() 方法还可以调用函数并且传递参数。这种情况下,我们可以使用 call() 方法传递多个参数,也可以使用 apply() 方法将参数放在一个数组中传递。

下面我们来看一个例子:

代码语言:javascript复制
function sum(a, b, c) {
  console.log(`The sum of ${a}, ${b} and ${c} is ${a   b   c}.`);
}

sum.call(null, 1, 2, 3);
sum.apply(null, [1, 2, 3]);

复制

在这个例子中,我们定义了一个函数 sum,它接受三个参数,可以计算它们的和。我们使用 call() 和 apply() 方法分别调用这个函数,并且传递相同的参数。在 call() 方法中,我们将参数一个一个传递,而在 apply() 方法中,我们将参数放在一个数组中传递。这两个方法的输出结果是相同的:

代码语言:javascript复制
The sum of 1, 2 and 3 is 6.

复制

总结

在 JavaScript 中,call() 和 apply() 方法是非常常用的。它们可以改变函数的上下文,借用其他对象的方法,以及调用函数并且传递参数。这两个方法的区别在于参数的传递方式不同。在大多数情况下,使用 call() 方法调用函数的性能要比使用 apply() 方法调用函数的性能要好。但是,这种差别在实际应用中并不是很明显,只有在调用函数的次数非常多的情况下才会产生明显的影响。

在使用 call() 和 apply() 方法时,需要注意参数的传递方式,以及 thisArg 参数的处理方式。如果不理解这些细节,很容易出现错误。因此,我们需要认真学习这两个方法的用法,掌握它们的细节,才能更好地使用它们。

0 人点赞