目录
一、new
new
的核心作用:基于构造函数创建实例,绑定原型链,初始化对象属性。
底层四步:创建空对象 → 绑定原型 → 执行构造函数 → 处理返回值
function objectFactory(constructor, ...args) {
// 严格校验构造函数
if (typeof constructor !== 'function') {
throw new TypeError('Constructor must be a function');
}
// 创建对象并绑定原型
const instance = Object.create(constructor.prototype);
// 执行构造函数并获取返回值
const result = constructor.apply(instance, args);
// 处理返回值(特别处理 null)
return result !== null &&
(typeof result === 'object' || typeof result === 'function')
? result
: instance;
}
// 构造函数返回对象的情况
function Car(model) {
this.model = model;
return { custom: 'return value' };
}
const car1 = new Car('Tesla');
const car2 = objectFactory(Car, 'BMW');
console.log('\n--- 构造函数返回对象测试 ---');
console.log('原生 new:', car1 instanceof Car, car1); // false { custom: 'return value' }
console.log('手写实现:', car2 instanceof Car, car2); // false { custom: 'return value' }
// 构造函数返回原始值的情况
function Bike(brand) {
this.brand = brand;
return 'This should be ignored';
}
const bike1 = new Bike('Yamaha');
const bike2 = objectFactory(Bike, 'Honda');
console.log('\n--- 构造函数返回原始值测试 ---');
console.log('原生 new:', bike1 instanceof Bike, bike1); // true Bike { brand: 'Yamaha' }
console.log('手写实现:', bike2 instanceof Bike, bike2); // true Bike { brand: 'Honda' }
局限性:
- 对内置包装对象(如
Number
,String
)的处理不完善 - 内置包装构造函数(
Number
,String
,Boolean
)在被函数式调用(如apply
)时,会尝试返回原始值而不是设置新对象的内部插槽 Symbol
,BigInt
等类型不能作为构造函数调用,会直接抛出错误Date
,RegExp
等内置类型在被函数式调用时有不同的初始化逻辑
改进思路:
- 特殊处理内置构造函数,直接使用
new
创建实例 - 针对特定类型(如
Symbol
)创建适配逻辑 - 完善返回值处理逻辑,考虑原始值返回的特殊情况
二、call
call:立即调用函数,指定 this
并逐个传递参数。
实现步骤:
- 处理上下文(context为null或undefined时指向全局对象)
- 创建唯一属性键,避免覆盖原有属性
- 将当前函数设置为上下文的方法
- 执行函数并保存结果
- 删除临时添加的方法
- 返回执行结果
Function.prototype.myCall = function (context, ...args) {
// 1. 处理上下文(context为null或undefined时指向全局对象)
context = context || globalThis;
// 2. 创建唯一属性键,避免覆盖原有属性
const fnKey = Symbol('fnKey');
// 3. 将当前函数设置为上下文的方法
context[fnKey] = this;
// 4. 执行函数并保存结果
const result = context[fnKey](...args);
// 5. 删除临时添加的方法
delete context[fnKey];
// 6. 返回执行结果
return result;
};
// 测试用例
function greet(greeting, punctuation) {
return `${greeting}, ${this.name}${punctuation}`;
}
const person = { name: 'Alice' };
console.log(greet.myCall(person, 'Hello', '!')); // "Hello, Alice!"
三、apply
apply:立即调用函数,指定 this
并以数组形式传递参数。
实现步骤:
- 处理上下文(context为null或undefined时指向全局对象)
- 创建唯一属性键,避免覆盖原有属性
- 将当前函数设置为上下文的方法
- 执行函数,处理参数数组
- 删除临时添加的方法
- 返回执行结果
Function.prototype.myApply = function(context, argsArray) {
// 1. 处理上下文
context = context || globalThis;
// 2. 创建唯一属性键
const fnKey = Symbol('fnKey');
// 3. 将当前函数设置为上下文的方法
context[fnKey] = this;
// 4. 执行函数(处理参数数组)
const result = argsArray
? context[fnKey](...argsArray)
: context[fnKey]();
// 5. 删除临时添加的方法
delete context[fnKey];
// 6. 返回执行结果
return result;
};
// 测试用例
function sum(a, b, c) {
return a + b + c + this.d;
}
const obj = { d: 10 };
console.log(sum.myApply(obj, [1, 2, 3])); // 16 (1+2+3+10)
四、bind
bind:返回一个绑定了 this
和预设参数的新函数,需手动调用。
Function.prototype.myBind = function (context, ...args) {
// 保存原始函数引用
const self = this;
// 返回绑定函数
return function boundFn(...newArgs) {
// 判断是否通过 new 调用
if (this instanceof boundFn) {
return new self(...args, ...newArgs);
}
// 普通调用使用绑定的上下文
return self.apply(context, [...args, ...newArgs]);
};
};
// 测试用例
function multiply(a, b, c) {
return a * b * c * this.d;
}
const bound = multiply.myBind({ d: 2 }, 3);
console.log(bound(4, 5)); // 3*4*5*2 = 120
// 测试 new 调用
function Person(name, age) {
this.name = name;
this.age = age;
}
const BoundPerson = Person.myBind(null, 'Alice');
const p = new BoundPerson(30);
console.log(p.name, p.age); // Alice 30
五、create
Object.create:用于创建一个新对象,并将现有对象作为新创建对象的原型。
function myCreate(proto, properties) {
if (typeof proto !== 'object' && proto !== null) {
throw new Error();
}
function F() {} // 空构造函数
F.prototype = proto; // 设置原型
const obj = new F(); // 创建新对象
// 可选:添加属性
if (properties) {
Object.defineProperties(obj, properties);
}
return obj;
}
// 测试
const person = { name: 'Alice' };
const me = myCreate(person);
console.log(me.name); // "Alice"(继承自 person)
console.log(Object.getPrototypeOf(me) === person); // true
ps:Object.create() 创建的对象只是原型指向源对象,并不会继承它的任何属性;而 new 出来的对象会继承原型对象的属性和方法。