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(返回构造函数的引用类型)
四、核心要点总结
new的核心是创建新对象 + 绑定原型 + 改变构造函数 this 指向;- 实例的
__proto__永远指向构造函数的prototype(这是原型链继承的基础); - 构造函数的返回值仅在为「引用类型」时,才会覆盖
new创建的新对象; - 箭头函数不能作为构造函数(无
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。