手写call,apply,bind,new

3 阅读2分钟

以下是手写的 callapplybindnew 实现:


1. 手写 call

Function.prototype.myCall = function(context, ...args) {
    // 如果 context 是 null 或 undefined,指向全局对象(浏览器中是 window,Node 中是 global)
    context = context || globalThis;
    
    // 创建一个唯一的属性名,避免覆盖原对象属性
    const fnSymbol = Symbol();
    
    // 将当前函数(this)赋值给这个临时属性
    context[fnSymbol] = this;
    
    // 执行函数并传入参数
    const result = context[fnSymbol](...args);
    
    // 删除临时属性
    delete context[fnSymbol];
    
    return result;
};

2. 手写 apply

Function.prototype.myApply = function(context, argsArray) {
    // 如果 context 是 null 或 undefined,指向全局对象
    context = context || globalThis;
    
    // 处理参数:如果 argsArray 不是数组或类数组,则当作空数组
    argsArray = argsArray ? [...argsArray] : [];
    
    const fnSymbol = Symbol();
    context[fnSymbol] = this;
    
    const result = context[fnSymbol](...argsArray);
    
    delete context[fnSymbol];
    
    return result;
};

3. 手写 bind

Function.prototype.myBind = function(context, ...boundArgs) {
    // 保存原函数
    const originalFunc = this;
    
    // 返回一个新函数
    return function(...callArgs) {
        // 合并参数:bind 时的参数 + 调用时的参数
        const allArgs = [...boundArgs, ...callArgs];
        
        // 处理 new 的情况:如果通过 new 调用,this 指向实例,不能绑定到 context
        // 判断 this 是否是 originalFunc 的实例(即是否使用 new 调用)
        if (new.target) {
            // new 调用时,原函数内部的 this 应该指向新创建的实例
            // 这里用 originalFunc 的 prototype 创建实例
            const instance = Object.create(originalFunc.prototype);
            const result = originalFunc.apply(instance, allArgs);
            // 如果原函数返回了一个对象,则返回那个对象,否则返回实例
            return result instanceof Object ? result : instance;
        } else {
            // 普通调用,绑定 context
            return originalFunc.apply(context, allArgs);
        }
    };
};

4. 手写 new

function myNew(Constructor, ...args) {
    // 1. 创建一个新对象,该对象的原型指向 Constructor 的 prototype
    const obj = Object.create(Constructor.prototype);
    
    // 2. 执行构造函数,并将 this 指向新创建的对象
    const result = Constructor.apply(obj, args);
    
    // 3. 判断构造函数是否返回了一个对象
    // 如果返回的是对象,则返回该对象;否则返回 obj
    return result instanceof Object ? result : obj;
}

测试代码

// 测试 call
function greet(greeting, punctuation) {
    console.log(`${greeting}, ${this.name}${punctuation}`);
}
const person = { name: 'Alice' };
greet.myCall(person, 'Hello', '!'); // 输出: Hello, Alice!

// 测试 apply
greet.myApply(person, ['Hi', '?']); // 输出: Hi, Alice?

// 测试 bind
const boundGreet = greet.myBind(person, 'Hey');
boundGreet('!!!'); // 输出: Hey, Alice!!!

// 测试 new
function Animal(name) {
    this.name = name;
}
Animal.prototype.say = function() {
    console.log(this.name);
};
const cat = myNew(Animal, 'Kitty');
cat.say(); // 输出: Kitty

关键点总结

方法核心要点
call临时给对象添加方法 → 执行 → 删除
applycall,但参数以数组传入
bind返回新函数,处理 new 调用和参数合并
new创建对象 → 绑定原型 → 执行构造函数 → 返回对象