Skip to content

3. 如何实现一个 new ​

难度等级:🟡 中级 分类:手写题 归档日期:2026-10-20


new 操作符主要做了 5 件事:

  1. 校验函数是否可构造[1];
  2. 创建对象并连接原型链;
  3. 绑定 this 到这个新对象;
  4. 执行函数体;
  5. 决定返回值。
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 的解法。


  1. 可构造指函数拥有内部方法 [[Construct]]。箭头函数、对象方法简写、async 函数、生成器函数都没有 [[Construct]],不能被 new。 ↩︎

个人面试题归档,持续更新中