前言
在 JavaScript 面向对象编程中,new 关键字是实例化对象的核心。面试官常常通过“手写 new”来考察你对原型链、this 绑定以及构造函数返回值的理解。本文将带你从原理到实现,彻底搞懂 new 背后的魔法。
一、 new 到底干了什么?
当我们使用 new Person() 时,JS 引擎在背后默默执行了以下 4 个步骤:
-
创建一个新对象:在内存中创建一个新的空对象(例如
obj = {})。 -
链接原型:将新对象的
__proto__属性指向构造函数的prototype,从而实现原型继承(让实例能访问原型上的方法)。 -
绑定 this:将构造函数内部的
this绑定到这个新对象上,并执行构造函数(为新对象添加属性)。 -
返回对象:
- 如果构造函数显式返回了一个对象(或函数),则返回该结果。
- 如果构造函数没有返回对象(返回基本类型或无返回值),则返回步骤 1 创建的新对象。
二、 手写 myNew 实现
根据上述原理,我们可以实现一个自己的 myNew 函数。
/**
* 手写 new 操作符
* @param {Function} Constructor 构造函数
* @param {...any} args 传递的参数
*/
function myNew(Constructor, ...args) {
// 1. 创建一个新对象,并将其原型指向构造函数的 prototype
const obj = Object.create(Constructor.prototype);
// 2. 将构造函数的 this 绑定到新对象上,并执行构造函数
const result = Constructor.apply(obj, args);
// 3. 处理返回值逻辑 (这是面试中最容易忽视的细节!)
// 如果构造函数返回的是对象(不为null)或函数,则返回该结果;否则返回新创建的 obj
if ((typeof result === 'object' && result !== null) || typeof result === 'function') {
return result;
}
// 4. 返回新对象
return obj;
}
测试用例:
function Person(name, age) {
this.name = name;
this.age = age;
// 情况 1: 没有返回值(默认返回 this)
}
function Student(name) {
this.name = name;
// 情况 2: 返回一个对象
return { name: 'Special Student', grade: 100 };
}
function NumberObj() {
this.a = 1;
// 情况 3: 返回一个基本类型
return 123;
}
// 测试 1:正常情况
const per = myNew(Person, 'Ouyang', 23);
console.log(per); // Person { name: 'Ouyang', age: 23 }
console.log(per instanceof Person); // true
// 测试 2:构造函数返回对象
const stu = myNew(Student, 'XiaoMing');
console.log(stu); // { name: 'Special Student', grade: 100 } (this 被忽略了)
// 测试 3:构造函数返回基本类型
const num = myNew(NumberObj);
console.log(num); // NumberObj { a: 1 } (返回值 123 被忽略)
三、 深度解析:返回值陷阱
这是面试中最常挖的坑。
-
场景 A:构造函数内部没有
return,或者return一个基本数据类型(Number, String, Boolean, null, undefined)。- 结果:
new操作符会忽略这个返回值,直接返回新创建的实例对象。
- 结果:
-
场景 B:构造函数内部
return一个引用类型(Object, Array, Function)。- 结果:
new操作符会直接返回这个引用类型,新创建的实例对象会被丢弃(且this上的属性赋值也会失效)。
- 结果:
四、 面试模拟题(挑战一下)
Q1:Object.create() 和 new 有什么区别?
参考回答:
new:不仅创建新对象并继承原型,还会执行构造函数,进行属性初始化。Object.create():只负责创建一个新对象并继承原型,不会执行构造函数。
Q2:为什么代码中建议使用 Object.create 而不是 obj.__proto__?
参考回答: __proto__ 是非标准属性(虽然浏览器支持),直接修改它会破坏 JS 引擎的优化,严重影响性能。Object.create() 是 ES5 标准方法,更规范且性能更好。
Q3:如果构造函数返回 null,new 出来的结果是什么?
参考回答: 结果是新创建的实例对象。 因为 typeof null === 'object',但 null 是个特殊值。在 new 的规范中,如果返回的是对象类型但值为 null,仍然会忽略它,返回实例对象。这就是为什么在手写代码中我们要判断 result !== null。
结语
手写 new 是前端基础能力的试金石。理解了这 4 个步骤,你不仅能轻松应对面试,还能更深刻地理解 JavaScript 的继承机制。
如果你觉得这篇笔记对你有帮助,欢迎点赞收藏! 🚀