返回

JS 高级技巧:手把手教你实现 apply、call 和 bind 函数

前端

前言

在 JavaScript 中,函数是一等公民,这意味着函数可以像变量一样被赋值、传递和返回。这种特性使得 JavaScript 非常灵活,也让函数的调用方式变得多种多样。

除了直接调用函数之外,我们还可以通过 apply、call 和 bind 这三个函数来间接调用函数。这三种函数都允许我们改变函数的执行上下文,从而实现一些有趣的效果。

语法和用法

apply()

apply() 函数的语法如下:

Function.prototype.apply(thisArg, argArray);
  • thisArg:指定函数的执行上下文。
  • argArray:一个包含要传递给函数参数的数组。

apply() 函数的作用是将函数的执行上下文设置为 thisArg,然后将 argArray 中的元素作为函数的参数来调用该函数。

举个例子,我们有一个函数 sum(),它可以计算一组数字的和:

function sum() {
  return [].reduce.apply(this, arguments);
}

我们可以使用 apply() 函数来调用 sum() 函数,并传入一个数字数组作为参数:

var numbers = [1, 2, 3, 4, 5];
var result = sum.apply(null, numbers);
console.log(result); // 15

在这个例子中,我们使用 null 作为 thisArg,这表示 sum() 函数将在全局作用域中执行。我们使用 numbers 数组作为 argArray,这表示 sum() 函数的参数将是 [1, 2, 3, 4, 5]

call()

call() 函数的语法如下:

Function.prototype.call(thisArg, arg1, arg2, ..., argN);
  • thisArg:指定函数的执行上下文。
  • arg1, arg2, ..., argN:要传递给函数的参数。

call() 函数的作用是将函数的执行上下文设置为 thisArg,然后将 arg1, arg2, ..., argN 作为函数的参数来调用该函数。

举个例子,我们有一个函数 greet(),它可以向某人打招呼:

function greet(name) {
  console.log(`Hello, ${name}!`);
}

我们可以使用 call() 函数来调用 greet() 函数,并传入一个名字作为参数:

greet.call(null, 'John'); // Hello, John!

在这个例子中,我们使用 null 作为 thisArg,这表示 greet() 函数将在全局作用域中执行。我们使用 'John' 作为参数,这表示 greet() 函数的参数将是 'John'

bind()

bind() 函数的语法如下:

Function.prototype.bind(thisArg, arg1, arg2, ..., argN);
  • thisArg:指定函数的执行上下文。
  • arg1, arg2, ..., argN:要传递给函数的参数。

bind() 函数的作用是将函数的执行上下文设置为 thisArg,并返回一个新的函数。这个新的函数在被调用时,它的执行上下文将是 thisArg,并且它的参数将是 arg1, arg2, ..., argN

举个例子,我们有一个函数 sum(),它可以计算一组数字的和:

function sum() {
  return [].reduce.apply(this, arguments);
}

我们可以使用 bind() 函数来创建一个新的函数 sum5(), 它可以计算 5 个数字的和:

var sum5 = sum.bind(null, 1, 2, 3, 4, 5);
var result = sum5();
console.log(result); // 15

在这个例子中,我们使用 null 作为 thisArg,这表示 sum() 函数将在全局作用域中执行。我们使用 1, 2, 3, 4, 5 作为参数,这表示 sum() 函数的参数将是 [1, 2, 3, 4, 5]

底层实现原理

apply() 和 call()

apply() 和 call() 函数的底层实现原理非常相似。它们都是通过修改函数的 [[Call]] 内部属性来实现的。

[[Call]] 内部属性是一个函数,它指定了函数在被调用时应该如何执行。apply() 和 call() 函数都会修改函数的 [[Call]] 内部属性,使其在被调用时使用指定的 thisArg 和参数来执行。

bind()

bind() 函数的底层实现原理与 apply() 和 call() 函数不同。它并不是通过修改函数的 [[Call]] 内部属性来实现的,而是通过创建一个新的函数来实现的。

bind() 函数会创建一个新的函数,这个新函数的执行上下文是 thisArg,并且它的参数是 arg1, arg2, ..., argN。当这个新函数被调用时,它的执行上下文和参数将被传递给原函数,原函数将使用这些参数来执行。

总结

apply()、call() 和 bind() 这三个函数都是非常强大的函数,它们可以让我们灵活地调用函数,并改变函数的执行上下文。通过对这些函数的深入理解,我们能够编写出更优雅、更健壮的 JavaScript 代码。

进一步阅读