文章

new运算符执行原理深度解析

拆解 new 运算符的四步内部流程,搞懂箭头函数为何不能 new,掌握手写 new 的面试技巧。

new运算符执行原理深度解析

一句话概括

new 不是魔法,是四步固定流程:空对象出生 → 挂原型链 → 借 this 执行 → 看返回值脸色。背熟这四步,手写 myNew 就是默写。


核心知识点

1. new 的四步内部流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function myNew(Constructor, ...args) {
  // ① 生个空对象,原型指向 Constructor.prototype
  const obj = Object.create(Constructor.prototype);

  // ② 以 obj 为 this 执行构造函数
  const ret = Constructor.apply(obj, args);

  // ③ 构造函数返回了对象?用它。否则用 obj
  return ret instanceof Object ? ret : obj;
}

// 验证
function Person(name) {
  this.name = name;
}
Person.prototype.greet = function () {
  return `Hi, ${this.name}`;
};

const p = myNew(Person, 'Alice');
console.log(p.greet());          // Hi, Alice
console.log(p instanceof Person); // true

2. 构造函数 return 的暗坑

1
2
3
4
5
6
function A() { this.x = 1; return 42; }        // return 基本类型
console.log(new A().x); // 1 → 忽略返回值

function B() { this.x = 1; return { y: 2 }; }  // return 对象
console.log(new B().x); // undefined → 用了返回值!
console.log(new B().y); // 2

面试官问”new 返回什么”——答案取决于构造函数 return 了什么。

3. 箭头函数不能 new

1
2
3
4
5
6
const Arrow = () => {};
Arrow.prototype; // undefined

try { new Arrow(); } catch (e) {
  console.log(e.message); // "Arrow is not a constructor"
}

双重死刑:没有 prototype(第二步走不通),没有自己的 this(第三步走不通)。

4. class 必须 new

1
2
3
4
class Foo {
  constructor() { this.x = 1; }
}
Foo(); // TypeError: Class constructor Foo cannot be invoked without 'new'

ES6 在 [[Call]] 内部埋了检查——new.target 为空就抛错。

5. new.target — 检测调用方式

1
2
3
4
5
6
7
function Foo() {
  if (!new.target) {
    throw new Error('请用 new 调用');
  }
}
Foo();        // Error
new Foo();    // OK

「其实你每天都在用」

1. 每一个 new Promise()

1
2
const p = new Promise((resolve) => resolve(42));
// ① {} → ② 挂 Promise.prototype → ③ 执行 executor → ④ 返回 p

2. 每一个 new Date() / new Map() / new Set()

所有内置构造器都遵循同一套四步法则。

3. React 的 new ErrorBoundary()

错误边界在内部也是 new 出来的实例。

4. 第三方库的单例模式

有些库利用 new 的返回值特性做单例:

1
2
3
4
5
6
7
let instance = null;
function Store(data) {
  if (instance) return instance; // 返回已有对象
  this.data = data;
  instance = this;
}
new Store('a') === new Store('b'); // true

5. Object.create(null) 创建”纯净字典”

没有 toStringhasOwnProperty 等原型方法,适合当 key-value 容器:

1
2
const dict = Object.create(null);
dict.toString = 'safe'; // 不会覆盖原型方法

常见误解(FAQ)

❌ 误区 1:「new 就是把函数当构造函数调用」

不只如此。new 改变了函数的执行语义:创建新对象、绑定原型、检查和替换返回值。普通调用 Person('Alice')thisundefined(严格模式)或 window

❌ 误区 2:「构造函数 return 基本类型没用」

对——但这是规范刻意设计的。如果构造函数 return 了对象,new 就放弃自己创建的对象,直接用返回值。这是有意为之,有些设计模式依赖这一点。

❌ 误区 3:「class 就是另一种 function」

语法上是,语义上更严格。class 有 [[IsClassConstructor]] 内部槽位,直接调用抛 TypeError。function 没有这个限制。

❌ 误区 4:「new 和 Object.create 是一回事」

Object.create(proto) 只做前两步(创建对象 + 绑定原型),不执行构造函数。这就是为什么寄生组合继承用它来避免 new Parent() 的副作用。


一句话总结

new 是 JS 里最微妙的语法糖——看着像调函数,实则走了一套精密的状态机;理解它,你才真正看懂了 JS 对象系统的入口。

本文由作者按照 CC BY 4.0 进行授权