面试常问:
new一个对象的时候, JS 到底做了什么? 能不能手写一个?
看着简单,但真让你写出 myNew(Fn, ...args),能一步搞对的人不多。这篇文章拆解 new 的 4 个内部步骤,一步步实现完整版本。
一、new 做了 4 件事
先说结论,new Fn(...args) 内部做了 4 步:
- 创建一个新对象
obj = {} - 把 obj 的原型指向 Fn.prototype(建立原型链)
- 执行 Fn,把 this 绑定到 obj
- 判断返回值:如果 Fn 返回对象则用它,否则返回 obj
理解这 4 步,手写就是把它们翻译成代码。
二、一步步实现
Step 1:创建新对象
function myNew(Fn, ...args) {
const obj = {};
// ...
}
Step 2:建立原型链
关键点:让 obj 能访问到 Fn.prototype 上的方法。
function myNew(Fn, ...args) {
const obj = {};
Object.setPrototypeOf(obj, Fn.prototype);
// 或者:obj.__proto__ = Fn.prototype;
}
为什么用 Object.setPrototypeOf 不用 __proto__?
__proto__ 是非标准属性(虽然主流引擎都支持),Object.setPrototypeOf 是 ES6 标准 API,更规范。
Step 3:执行构造函数,绑定 this
function myNew(Fn, ...args) {
const obj = {};
Object.setPrototypeOf(obj, Fn.prototype);
const result = Fn.apply(obj, args);
// ...
}
用 apply 把 this 绑到 obj 上,同时收集参数。
Step 4:处理返回值
这一步是最容易被忽略的关键。
function myNew(Fn, ...args) {
const obj = {};
Object.setPrototypeOf(obj, Fn.prototype);
const result = Fn.apply(obj, args);
// 如果构造函数返回对象,就用它;否则用 obj
return result instanceof Object ? result : obj;
}
为什么要判断返回值?
因为 JS 允许构造函数显式返回一个对象,这时 new 会用返回的对象,而不是新创建的 obj。
function Person(name) {
this.name = name;
return { age: 18 }; // 显式返回对象
}
const p = new Person('张三');
console.log(p); // { age: 18 } —— 用了返回值,不是新 obj
console.log(p.name); // undefined —— name 丢了
如果返回的是基础类型(字符串、数字、布尔、undefined、null),new 会忽略,继续返回新对象。
function Person(name) {
this.name = name;
return 'hello'; // 返回基础类型
}
const p = new Person('张三');
console.log(p.name); // '张三' —— 返回值被忽略了
三、完整版
function myNew(Fn, ...args) {
// 1. 创建新对象,建立原型链(可以一步合并)
const obj = Object.create(Fn.prototype);
// 2. 执行构造函数,绑定 this
const result = Fn.apply(obj, args);
// 3. 处理返回值
return result instanceof Object ? result : obj;
}
Object.create(proto) 一步完成"创建对象 + 设置原型",更简洁。
四、测试
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayHi = function() {
return `Hi, I'm ${this.name}`;
};
// 用原生 new
const p1 = new Person('张三', 25);
// 用手写 myNew
const p2 = myNew(Person, '张三', 25);
console.log(p1.name === p2.name); // true
console.log(p1.sayHi() === p2.sayHi()); // true
console.log(p2 instanceof Person); // true
验证返回值处理:
function Cat(name) {
this.name = name;
return { type: 'cat' };
}
const c1 = new Cat('小花');
const c2 = myNew(Cat, '小花');
console.log(c1); // { type: 'cat' }
console.log(c2); // { type: 'cat' } —— 一致
五、进阶:instanceof Object 的坑
上面用 result instanceof Object 判断是否返回对象,其实还有一个更严谨的写法:
return (typeof result === 'object' && result !== null) ||
typeof result === 'function'
? result
: obj;
为什么?
typeof null === 'object',但 null 不是对象,要排除- 函数也是对象,如果构造函数返回一个函数,也应该用返回值
不过面试时用 instanceof Object 已经能过关,进阶版了解就行。
六、面试怎么答
面试官:"手写一个 new"
分层答法:
- 先说清楚 new 做了 4 件事(展示理解)
- 写出核心 3 行代码(展示动手)
- 补充返回值处理(展示细节)
- 说明基础类型和对象的返回差异(展示深度)
function myNew(Fn, ...args) {
const obj = Object.create(Fn.prototype);
const result = Fn.apply(obj, args);
return result instanceof Object ? result : obj;
}
能一步步讲清楚 = 加分,直接甩终极代码 = 面试官会觉得你在背。
总结
new 内部就 4 步:
- 创建对象
- 建立原型链
- 绑定 this 执行
- 判断返回值
15 行代码手写完成。关键在于第 4 步的返回值判断 —— 这是区分"背过"和"真理解"的分水岭。