call、apply、bind 用法及实现
一、核心概念
三者都是 JavaScript 中函数的方法,用于改变函数执行时的 this 指向。
二、基本用法对比
| 方法 | 传参方式 | 执行时机 | 返回值 |
|---|---|---|---|
call | 参数列表 | 立即执行 | 函数执行结果 |
apply | 数组参数 | 立即执行 | 函数执行结果 |
bind | 参数列表 | 返回新函数,可延迟执行 | 绑定 this 的新函数 |
三、详细用法
1. call
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const person = { name: 'Alice' };
// 用法
greet.call(person, 'Hello', '!'); // 输出: Hello, Alice!
2. apply
function sum(a, b, c) {
return a + b + c;
}
const numbers = [1, 2, 3];
// 用法
const result = sum.apply(null, numbers); // 输出: 6
console.log(result);
// 实际应用:求数组最大值
const arr = [1, 5, 3, 9, 2];
const max = Math.max.apply(null, arr); // 9
3. bind
function multiply(x, y) {
return x * y;
}
const double = multiply.bind(null, 2); // 柯里化:固定第一个参数为2
console.log(double(5)); // 10
console.log(double(8)); // 16
四、手动实现
1. 实现 call
Function.prototype.myCall = function(context, ...args) {
// 处理 context 为 null 或 undefined 的情况
context = context || window || global;
// 创建唯一键,避免属性覆盖
const fnKey = Symbol('fn');
// 将当前函数作为 context 的方法
context[fnKey] = this;
// 执行函数
const result = context[fnKey](...args);
// 删除临时添加的属性
delete context[fnKey];
return result;
};
// 测试
function testCall(a, b) {
console.log(this.name, a + b);
return a + b;
}
const obj = { name: 'Test' };
testCall.myCall(obj, 1, 2); // 输出: Test 3
2. 实现 apply
Function.prototype.myApply = function(context, argsArray) {
context = context || window || global;
const fnKey = Symbol('fn');
context[fnKey] = this;
// 处理参数,apply 接收数组参数
const result = argsArray ?
context[fnKey](...argsArray) :
context[fnKey]();
delete context[fnKey];
return result;
};
// 测试
function testApply(a, b, c) {
console.log(this.name, a + b + c);
return a + b + c;
}
testApply.myApply(obj, [1, 2, 3]); // 输出: Test 6
3. 实现 bind
Function.prototype.myBind = function(context, ...bindArgs) {
const self = this;
// 返回的新函数
const boundFunction = function(...callArgs) {
// 判断是否通过 new 调用
const isNewCall = this instanceof boundFunction;
// 如果是 new 调用,this 指向新创建的对象
// 否则使用绑定的 context
const thisContext = isNewCall ? this : context;
return self.apply(thisContext, [...bindArgs, ...callArgs]);
};
// 维护原型关系
boundFunction.prototype = Object.create(self.prototype);
return boundFunction;
};
// 测试
function Person(name, age) {
this.name = name;
this.age = age;
}
const BoundPerson = Person.myBind(null, 'John');
const p = new BoundPerson(25);
console.log(p.name, p.age); // 输出: John 25
五、实际应用场景
六、注意事项
- 严格模式下:如果 context 是 null 或 undefined,this 将指向 undefined
- 性能考虑:频繁使用 call/apply 可能影响性能
- 箭头函数:没有自己的 this,call/apply/bind 对箭头函数无效
- bind 多次绑定:多次 bind 只有第一次有效