call、apply 和 bind 的区别
这三个方法都属于 Function.prototype,用于改变函数的 this 指向,但它们的用法和行为有所不同:
-
call: 调用函数时,立即执行,并通过逗号分隔的方式传递参数。
func.call(thisArg, arg1, arg2, ...)
- apply: 调用函数时,立即执行,并以数组形式传递参数。
func.apply(thisArg, [arg1, arg2, ...])
- bind: 返回一个新的函数,该函数的 this 永久绑定为指定的对象,并且可以接收参数。
const boundFunc = func.bind(thisArg, arg1, arg2, ...);
call 示例
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const person = { name: 'Alice' };
greet.call(person, 'Hello', '!');
apply 示例
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const person = { name: 'Alice' };
greet.apply(person, ['Hi', '.']);
bind 示例
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const person = { name: 'Alice' };
const boundGreet = greet.bind(person, 'Hey', '!');
boundGreet(); // 输出: Hey, Alice!
手写实现 call、apply 和 bind
call 的实现
Function.prototype.myCall = function (thisArg, ...args) {
// 如果 thisArg 为 null 或 undefined,默认指向全局对象(浏览器为 window,Node.js 为 globalThis)
thisArg = thisArg || globalThis;
// 使用 Symbol 防止覆盖已有属性
const fnKey = Symbol('fn');
thisArg[fnKey] = this;
// 调用函数并存储结果
const result = thisArg[fnKey](...args);
// 删除临时属性
delete thisArg[fnKey];
return result;
};
apply 的实现
Function.prototype.myApply = function (thisArg, args) {
// 如果 thisArg 为 null 或 undefined,默认指向全局对象
thisArg = thisArg || globalThis;
// 使用 Symbol 防止覆盖已有属性
const fnKey = Symbol('fn');
thisArg[fnKey] = this;
// 调用函数并存储结果
const result = thisArg[fnKey](...(args || []));
// 删除临时属性
delete thisArg[fnKey];
return result;
};
bind 的实现
Function.prototype.myBind = function (thisArg, ...bindArgs) {
const originalFunc = this;
return function boundFunction(...callArgs) {
// thisArg 为 null 或 undefined 时,默认指向全局对象
return originalFunc.apply(thisArg || globalThis, [...bindArgs, ...callArgs]);
};
};
//测试代码
function testFunc(arg1, arg2) {
console.log(this.value, arg1, arg2);
}
const obj = { value: 42 };
// 测试 myCall
testFunc.myCall(obj, 'hello', 'world'); // 输出: 42 hello world
// 测试 myApply
testFunc.myApply(obj, ['foo', 'bar']); // 输出: 42 foo bar
// 测试 myBind
const boundFunc = testFunc.myBind(obj, 'bindArg');
boundFunc('callArg'); // 输出: 42 bindArg callArg