手写call、apply 和 bind

94 阅读1分钟

call、apply 和 bind 的区别

这三个方法都属于 Function.prototype,用于改变函数的 this 指向,但它们的用法和行为有所不同:

  1. call: 调用函数时,立即执行,并通过逗号分隔的方式传递参数。

func.call(thisArg, arg1, arg2, ...)
  1. apply: 调用函数时,立即执行,并以数组形式传递参数。
func.apply(thisArg, [arg1, arg2, ...])
  1. 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