Appearance
3. 如何实现一个 new
难度等级:🟡 中级 分类:手写题 归档日期:2026-10-20
new 操作符主要做了 5 件事:
- 校验函数是否可构造[1];
- 创建对象并连接原型链;
- 绑定
this到这个新对象; - 执行函数体;
- 决定返回值。
javascript
function myNew(Ctor, ...args) {
// 1. 校验函数是否可构造
if (!isConstructor(Ctor)) {
throw new TypeError(`${String(Ctor)} is not a constructor`);
}
// 2. 创建对象并连接原型链
// prototype 不是对象时(被改成了原始值),规范规定回退到 Object.prototype
const proto = Ctor.prototype;
const obj = Object.create(
proto !== null && (typeof proto === 'object' || typeof proto === 'function')
? proto
: Object.prototype
);
// 3 + 4. 绑定 this 到新对象,并执行函数体
const result = Ctor.apply(obj, args);
// 5. 决定返回值:构造函数显式返回对象(含函数)则用它,否则返回新对象
const isObjectLike =
result !== null && (typeof result === 'object' || typeof result === 'function');
return isObjectLike ? result : obj;
}
/**
* 判断是否可构造(是否有内部的 [[Construct]] 方法)
* 用 Reflect.construct 的第三个参数 newTarget 做探测:
* newTarget 必须是构造器,否则抛 TypeError,且不会真正执行 Ctor
*/
function isConstructor(fn) {
if (typeof fn !== 'function') return false;
try {
Reflect.construct(String, [], fn);
return true;
} catch {
return false;
}
}局限:不支持 class
isConstructor 中的 Reflect.construct(String, [], fn) 只是把 fn 作为 newTarget 来探测它有没有 [[Construct]],并不会执行 fn。class 有 [[Construct]],所以能通过校验。
但第 3、4 步用的是 Ctor.apply(obj, args),即把构造函数当普通函数调用。而 class 的构造函数规定只能通过 new 调用,所以会报错:
javascript
class A {
constructor(x) {
this.x = x;
}
}
myNew(A, 1); // TypeError: Class constructor A cannot be invoked without 'new'如果要支持 class,可以把执行阶段换成 Reflect.construct(Ctor, args),但这等于把创建对象、绑定 this、执行函数体都交给引擎完成,手写 new 也就失去了展示原理的意义。所以面试中一般用 Object.create + apply 展示原理,再补充说明这个局限和 Reflect.construct 的解法。
可构造指函数拥有内部方法
[[Construct]]。箭头函数、对象方法简写、async函数、生成器函数都没有[[Construct]],不能被new。 ↩︎