JavaScript中apply、call、bind区别

219 阅读4分钟

JavaScript中apply、call、bind区别

在 JavaScript 中,applycall 和 bind 都是用于改变函数执行时的上下文(即 this 的指向)的方法,它们有一些共同点,也存在明显区别:

相同点

  1. 作用一致:三者的核心功能都是改变函数执行时 this 的指向。
  2. 第一个参数相同:第一个参数都是指定函数执行时 this 要指向的对象(若为 null 或 undefined,则 this 会指向全局对象window,在严格模式下为 undefined)。

不同点

主要区别在于 参数传递方式 和 执行时机

方法参数传递方式执行时机返回值
apply以数组形式传递参数,第二个参数为数组立即执行函数函数执行的返回值
call逐个传递参数,第一个参数后直接跟参数列表立即执行函数函数执行的返回值
bind逐个传递参数(同 call不立即执行,返回新函数绑定了 this 的新函数
  • 若需要 立即执行函数,且参数是 数组形式,用 apply
  • 若需要 立即执行函数,且参数是 逐个传递,用 call
  • 若需要 延迟执行函数(如事件回调、定时器),用 bind,它会返回一个绑定了 this 的新函数。

示例说明

假设有一个函数和一个对象:

function greet(message) {
  console.log(`${message}, ${this.name}`);
}

const person = { name: "Alice" };
  1. apply 的使用
greet.apply(person, ["Hello"]); // 输出:Hello, Alice
greet.apply(person, ["1","2"]); // 输出:Hello, Alice,1,2
// 第二个参数必须是数组(或类数组对象),数组元素作为函数参数
  1. call 的使用
greet.call(person, "Hello"); // 输出:Hello, Alice
greet.call(person, "Hello","1","2"); // 输出:Hello, Alice,1,2
greet.call(person, "Hello",["1","2"]); // 输出:Hello, Alice,[["1","2"]]
// 第一个参数是 this 指向的对象,后续参数逐个传入
  1. bind 的使用
const boundGreet = greet.bind(person, "Hello");
boundGreet(); // 输出:Hello, Alice
boundGreet(1,2); // 输出:Hello, Alice,1,2
boundGreet([1,2]); // 输出:Hello, Alice,[[1,2]]
// 不会立即执行,而是返回一个新函数,调用新函数时才执行

总结

  • 三者都可以改变函数的this对象指向;
  • 三者第一个参数都是this要指向的对象,如果没有这个参数或参数为undefined或者null,则默认指向全局window
  • 三者都可以传参,但是apply是数组,而callbind是参数列表,且applycall是一次性传入参数,而bind可以分为多次传入;
  • bind是返回绑定this之后的函数,不会立即执行,而applycall则立即执行。

bind可分为多次传入怎么理解

在 bind 方法中,"可以分多次传入参数" 指的是:第一次调用 bind 时可以传入部分参数,后续调用返回的新函数时再传入剩余参数,最终这些参数会按顺序合并传递给原函数。

这种特性称为 参数柯里化(Currying) ,即把接收多个参数的函数拆分成一系列接收单一参数的函数。

示例说明

假设我们有一个计算总和的函数:

function sum(a, b, c) {
  return a + b + c;
}

使用 bind 分多次传入参数:

// 第一次 bind:绑定 this(这里用 null 表示不改变 this),并传入第一个参数 a=1
const sum1 = sum.bind(null, 1);

// 调用 sum1 时传入第二个参数 b=2
const sum2 = sum1.bind(null, 2); // 此时还未传完参数,函数等待第三个参数

// 再传入第三个参数 c=3,完成所有参数传递
const result = sum2(3); 

console.log(result); // 输出:6(1+2+3)

分步解析

  1. sum.bind(null, 1) :

    • 绑定 this 为 null(非严格模式下指向全局对象)
    • 预先传入第一个参数 a=1
    • 返回一个新函数 sum1,此时它还需要接收 b 和 c
  2. sum1.bind(null, 2) :

    • 调用 sum1 时传入第二个参数 b=2
    • 此时函数仍等待第三个参数 c
    • 等价于 sum(1, 2, ?)
  3. sum2(3) :

    • 传入最后一个参数 c=3
    • 所有参数齐全,执行原函数 sum(1, 2, 3)

与 call/apply 的区别

call 和 apply 必须在调用时一次性传完所有参数,而 bind 支持 参数分批传递,这在需要延迟执行且参数无法一次性获取的场景中非常有用(例如事件监听、异步操作)。

例如在事件回调中分步传参:

function handleClick(event, prefix) {
  console.log(prefix, event.target);
}

// 先绑定 prefix 参数,事件对象会在触发时自动传入
const button = document.querySelector('button');
button.addEventListener('click', handleClick.bind(null, '点击了:'));

这里 bind 预先传入了 prefix='点击了:',而事件对象 event 会在点击时由浏览器自动作为后续参数传入。