JavaScript—new操作符具体干了什么

3 阅读4分钟

new 是 JavaScript 中用于创建构造函数实例的操作符,其核心作用是「创建一个新对象,并将该对象与构造函数的原型链关联,最终让构造函数作用于这个新对象」。

以下是 new 操作符执行的5 个核心步骤,附详细拆解和手动模拟实现:

一、new 操作符的完整执行流程

当执行 const obj = new Func(xxx) 时,JS 引擎会按顺序完成以下操作:

步骤 1:创建一个空的普通对象

首先创建一个全新的空对象({}),这个对象就是未来的构造函数实例。

const obj = {};

步骤 2:将新对象的原型指向构造函数的 prototype

让新对象继承构造函数原型上的属性和方法(这是实例能访问 Func.prototype 成员的核心)。

obj.__proto__ = Func.prototype;
// 等价于(更规范的写法):Object.setPrototypeOf(obj, Func.prototype)

步骤 3:将构造函数的 this 绑定到新对象

执行构造函数,且构造函数内部的 this 指向步骤 1 创建的空对象 —— 这样构造函数中通过 this.xxx 定义的属性 / 方法,都会挂载到新对象上。

const result = Func.call(obj, xxx); // 传入构造函数的参数

步骤 4:判断构造函数的返回值

构造函数的返回值会影响 new 的最终结果:

  • 如果构造函数返回一个引用类型(对象、数组、函数等),则 new 操作符最终返回这个引用类型,而非步骤 1 创建的新对象;
  • 如果构造函数无返回值,或返回基本类型(数字、字符串、布尔、null、undefined),则 new 操作符返回步骤 1 创建的新对象(这是最常见的场景)。

步骤 5:返回最终对象

根据步骤 4 的判断结果,返回对应的对象(绝大多数情况返回步骤 1 创建的新对象)。

二、直观示例:拆解 new 的执行过程

// 定义构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
  // 无显式返回值(等价于返回 undefined)
}
// 原型上挂载方法
Person.prototype.sayHi = function() {
  console.log(`我是${this.name}${this.age}岁`);
};

// 执行 new 操作
const p = new Person('张三', 20);

// 拆解 new 的执行步骤:
// 1. 创建空对象:const p = {};
// 2. 绑定原型:p.__proto__ = Person.prototype;
// 3. 执行构造函数:Person.call(p, '张三', 20) → p.name = '张三', p.age = 20;
// 4. 构造函数返回 undefined(基本类型),因此返回 p;
// 5. 最终 p 是 Person 的实例,能访问原型方法:p.sayHi() → 我是张三,20岁

特殊场景:构造函数返回引用类型

function Person(name) {
  this.name = name;
  // 返回一个新对象(引用类型)
  return { msg: '我是返回的对象' };
}
const p = new Person('张三');
console.log(p.name); // undefined(原对象被覆盖)
console.log(p.msg); // 我是返回的对象(new 返回了构造函数的返回值)

三、手动模拟实现 new 操作符

通过函数模拟 new 的核心逻辑,加深理解:

function myNew(constructor, ...args) {
  // 步骤1:创建空对象
  const obj = {};

  // 步骤2:绑定原型(兼容构造函数 prototype 为 null 的情况)
  if (constructor.prototype !== null) {
    obj.__proto__ = constructor.prototype;
  }

  // 步骤3:执行构造函数,绑定 this
  const result = constructor.apply(obj, args);

  // 步骤4+5:判断返回值,返回最终对象
  // 核心:如果构造函数返回引用类型,返回该值;否则返回 obj
  return (typeof result === 'object' && result !== null) || typeof result === 'function' 
    ? result 
    : obj;
}

// 测试模拟的 new
function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.sayHi = function() {
  console.log(`我是${this.name}`);
};

const p1 = myNew(Person, '李四', 25);
console.log(p1.name); // 李四
console.log(p1.age); // 25
p1.sayHi(); // 我是李四(能访问原型方法)

// 测试返回引用类型的情况
function Test() {
  this.a = 1;
  return { b: 2 };
}
const p2 = myNew(Test);
console.log(p2.a); // undefined
console.log(p2.b); // 2(返回构造函数的引用类型)

四、核心要点总结

  1. new 的核心是创建新对象 + 绑定原型 + 改变构造函数 this 指向
  2. 实例的 __proto__ 永远指向构造函数的 prototype(这是原型链继承的基础);
  3. 构造函数的返回值仅在为「引用类型」时,才会覆盖 new 创建的新对象;
  4. 箭头函数不能作为构造函数(无 prototype,且 this 不可改变),因此不能用 new 调用。

五、常见面试延伸问题

  • **Q:new 一个构造函数时,如果构造函数没有 prototype 会怎样?
  • **A:新对象的 __proto__ 会指向 Object.prototype(因为 obj.__proto__ = null 时,默认继承 Object 原型),实例仍能访问 toString 等 Object 原型方法。
  • **Q:new Func () 和 Func () 有什么区别?
  • **A:new Func() 会创建新对象,this 指向新对象;直接 Func() 中 this 指向全局(浏览器为 window,Node.js 为 global),且不会创建原型关联的实例。
  • **Q:为什么 new 操作符执行后,实例 instanceof 构造函数为 true?
  • **A:instanceof 会检查实例的原型链中是否存在构造函数的 prototype,而 new 已将实例的 __proto__ 绑定到构造函数的 prototype,因此返回 true。