在 JavaScript 中,bind、call 和 apply 是用于改变函数执行上下文(即 this 指向)的核心方法。以下是它们的核心区别与用法:
1. 执行时机与返回值
| 方法 | 执行时机 | 返回值 |
|---|---|---|
call | 立即执行原函数 | 返回原函数的执行结果 |
apply | 立即执行原函数 | 返回原函数的执行结果 |
bind | 不立即执行,返回新函数 | 返回绑定 this 后的新函数,需手动调用 |
示例:
const obj = { name: "Alice" };
function greet() { console.log(this.name); }
// call/apply 立即执行
greet.call(obj); // 输出: Alice
greet.apply(obj); // 输出: Alice
// bind 返回新函数
const boundGreet = greet.bind(obj);
boundGreet(); // 输出: Alice
2. 参数传递方式
| 方法 | 参数形式 |
|---|---|
call | 逐个传递:fn.call(thisArg, arg1, arg2) |
apply | 数组传递:fn.apply(thisArg, [args]) |
bind | 支持分步传参:绑定时可预置参数,调用时补充剩余参数 |
示例:
function sum(a, b) { return a + b; }
// call 逐参数传递
sum.call(null, 1, 2); // 返回: 3
// apply 数组传递
sum.apply(null, [1, 2]); // 返回: 3
// bind 分步传参
const boundSum = sum.bind(null, 1);
boundSum(2); // 返回: 3
3. 应用场景详解(含代码示例)
call 和 apply 的适用场景
1). 立即调用函数:借用其他对象的方法
场景说明:当某个对象没有某个方法,但其他对象有时,可以通过 call/apply 借用其方法。
示例:操作类数组对象(如 arguments 或 DOM 节点列表)的数组方法。
// 借用 Array.prototype.slice 将类数组转为数组
function toArray() {
return Array.prototype.slice.call(arguments); // 或 apply 直接传递 arguments
}
const arr = toArray(1, 2, 3);
console.log(arr); // [1, 2, 3]
// 操作 DOM 节点列表
const domNodes = document.querySelectorAll('div');
const nodeList = Array.prototype.map.call(domNodes, node => node.textContent);
2). 参数动态性:处理不定数量参数
场景说明:apply 可将数组参数展开为独立参数,适用于参数数量不固定的场景。
示例:计算数组最大值或合并数组:
// 使用 apply 展开数组参数
const numbers = [5, 6, 2, 3, 7];
const max = Math.max.apply(null, numbers); // 7
// 合并数组
const arr1 = [1, 2];
const arr2 = [3, 4];
Array.prototype.push.apply(arr1, arr2); // arr1 变为 [1, 2, 3, 4]
bind 的适用场景
1). 延迟执行:固定上下文(避免丢失 this)
场景说明:在异步回调(如事件监听、定时器)中,使用 bind 固定 this,避免因调用方式改变上下文。
示例:点击事件或 setTimeout:
// 点击事件中固定 this
const button = document.querySelector('button');
const obj = {
message: 'Clicked!',
handleClick: function() {
console.log(this.message);
}
};
button.addEventListener('click', obj.handleClick.bind(obj)); // 点击输出 "Clicked!"
// setTimeout 中固定 this
const timerObj = {
value: 10,
start: function() {
setTimeout(function() {
console.log(this.value); // 未绑定则输出 undefined
}.bind(this), 1000); // 绑定后输出 10
}
};
timerObj.start();
2). 柯里化(Currying):预置参数创建新函数
场景说明:通过 bind 预先设置部分参数,生成一个参数更少的新函数。
示例:通用函数转换为特定功能函数:
// 预置参数生成新函数
function multiply(a, b) {
return a * b;
}
const double = multiply.bind(null, 2); // 固定第一个参数为 2
console.log(double(5)); // 10 (2 * 5)
// 动态生成问候语
function greet(greeting, name) {
return `${greeting}, ${name}!`;
}
const sayHello = greet.bind(null, 'Hello');
console.log(sayHello('Alice')); // "Hello, Alice!"
总结对比
| 方法 | 场景 | 代码示例 |
|---|---|---|
call | 明确参数数量、借用方法 | Array.slice.call(arguments) |
apply | 动态参数、展开数组 | Math.max.apply(null, [1,2,3]) |
bind | 固定上下文、参数预置(柯里化) | setTimeout(func.bind(obj), 1000) |
核心差异:
call/apply立即执行,适合直接调用或参数动态传递;bind延迟执行,适合需要保留上下文或参数预置的场景。
4. 其他特性
- 兼容性:
bind是 ES5 新增方法,不支持 IE8 及以下浏览器,而call和apply兼容性更好。 this绑定的持久性:bind返回的函数 永久绑定this,而call/apply仅临时修改一次。
总结对比表
| 特性 | call | apply | bind |
|---|---|---|---|
| 执行时机 | 立即执行 | 立即执行 | 返回新函数 |
| 参数形式 | 逐个传递 | 数组传递 | 分步预置 + 补充 |
| 返回值 | 函数执行结果 | 函数执行结果 | 绑定后的新函数 |
| 典型用途 | 继承、方法借用 | 处理数组参数 | 事件绑定、柯里化 |
通过以上分析,可根据实际需求选择合适的方法:需 立即执行且参数明确 时用 call/apply,需 延迟调用或固定上下文 时用 bind。
735

被折叠的 条评论
为什么被折叠?



