JavaScript中apply、call、bind区别
在 JavaScript 中,apply、call 和 bind 都是用于改变函数执行时的上下文(即 this 的指向)的方法,它们有一些共同点,也存在明显区别:
相同点
- 作用一致:三者的核心功能都是改变函数执行时
this的指向。 - 第一个参数相同:第一个参数都是指定函数执行时
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" };
- apply 的使用:
greet.apply(person, ["Hello"]); // 输出:Hello, Alice
greet.apply(person, ["1","2"]); // 输出:Hello, Alice,1,2
// 第二个参数必须是数组(或类数组对象),数组元素作为函数参数
- 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 指向的对象,后续参数逐个传入
- 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是数组,而call和bind是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入; bind是返回绑定this之后的函数,不会立即执行,而apply和call则立即执行。
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)
分步解析
-
sum.bind(null, 1):- 绑定
this为null(非严格模式下指向全局对象) - 预先传入第一个参数
a=1 - 返回一个新函数
sum1,此时它还需要接收b和c
- 绑定
-
sum1.bind(null, 2):- 调用
sum1时传入第二个参数b=2 - 此时函数仍等待第三个参数
c - 等价于
sum(1, 2, ?)
- 调用
-
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 会在点击时由浏览器自动作为后续参数传入。