以下是手写的 call、apply、bind 和 new 实现:
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 | 临时给对象添加方法 → 执行 → 删除 |
apply | 同 call,但参数以数组传入 |
bind | 返回新函数,处理 new 调用和参数合并 |
new | 创建对象 → 绑定原型 → 执行构造函数 → 返回对象 |