【前端面试小册】JS-第22节:实现 new
一、new 的概述
1.1 new 的作用
new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。
1.2 new 的过程
当使用 new 调用构造函数时,会执行以下步骤:
- 创建一个空对象
- 将空对象的
__proto__指向构造函数的prototype - 将构造函数的
this指向这个新对象,并执行构造函数 - 返回新对象(如果构造函数返回对象,则返回该对象;否则返回新创建的对象)
翻译过来就是我们需要:
- 创建一个空对象
let obj = {} - 将
obj.__proto__指向构造函数.prototype- 原型链作用:当对象上没有的属性会顺着
obj.__proto__去找
- 原型链作用:当对象上没有的属性会顺着
- 改变
this指向obj,并将参数传给构造函数 - 返回
null或者undefined不处理,如果函数返回值是一个对象obj,那么实例化后的对象就是这个obj
1.3 new 的使用示例
示例 1:基本使用
function User(age) {
this.age = age;
}
let user = new User(1);
console.log(user.age); // 1
示例 2:构造函数返回对象
function User1(age) {
this.age = age;
return {
age: 99
};
}
let user1 = new User1(1);
console.log(user1.age); // 99
注意:这里当函数返回一个对象时,this 将指向这个对象,实例化后的对象就是这个返回的对象。
示例 3:构造函数返回基本类型
function User2(age) {
this.age = age;
return 123; // 返回基本类型
}
let user2 = new User2(1);
console.log(user2.age); // 1(忽略返回值)
console.log(user2); // User2 { age: 1 }
注意:如果构造函数返回基本类型,会被忽略,仍然返回新创建的对象。
二、实现 new
2.1 实现思路
根据概述 new 的过程和特性,我们可以得到下面的实现代码:
function myNew(fn, ...args) {
// 1. 新建一个空对象,对象的原型为构造函数的 prototype 对象
// 等价于 let obj = {},obj.__proto__ = fn.prototype
let obj = Object.create(fn.prototype);
// 2. 改变指向和参数传递
const res = fn.apply(obj, args);
// 3. 判断返回值
// 如果构造函数返回对象,则返回该对象;否则返回新创建的对象
return res instanceof Object ? res : obj;
}
2.2 执行流程
graph TD
A[调用 myNew] --> B[创建空对象]
B --> C[设置原型链]
C --> D[执行构造函数]
D --> E{构造函数返回值类型}
E -->|对象| F[返回该对象]
E -->|基本类型/null/undefined| G[返回新创建的对象]
2.3 关键点解析
2.3.1 Object.create 的作用
let obj = Object.create(fn.prototype);
等价于:
let obj = {};
obj.__proto__ = fn.prototype;
作用:
- 创建新对象并设置原型链
- 使新对象可以访问构造函数原型上的属性和方法
2.3.2 apply 的作用
const res = fn.apply(obj, args);
作用:
- 将构造函数的
this指向新创建的对象 - 执行构造函数,初始化对象属性
2.3.3 返回值处理
return res instanceof Object ? res : obj;
逻辑:
- 如果构造函数返回对象,返回该对象
- 如果返回基本类型或
null/undefined,返回新创建的对象
注意:null instanceof Object 为 false,所以 null 会被忽略。
2.4 完整实现(带错误处理)
function myNew(fn, ...args) {
// 参数验证
if (typeof fn !== 'function') {
throw new TypeError('myNew: first argument must be a function');
}
// 1. 创建新对象,设置原型链
let obj = Object.create(fn.prototype);
// 2. 执行构造函数,绑定 this
const res = fn.apply(obj, args);
// 3. 判断返回值
// 如果构造函数返回对象,则返回该对象;否则返回新创建的对象
return res instanceof Object ? res : obj;
}
三、测试用例
3.1 基本测试
function fn(name) {
this.name = name;
}
const res = myNew(fn, '愚公上岸说');
console.log(res); // fn { name: '愚公上岸说' }
console.log(res instanceof fn); // true
3.2 返回对象测试
function User(age) {
this.age = age;
return {
age: 99,
name: '愚公上岸说'
};
}
const user = myNew(User, 1);
console.log(user); // { age: 99, name: '愚公上岸说' }
console.log(user.age); // 99
3.3 返回基本类型测试
function User(age) {
this.age = age;
return 123;
}
const user = myNew(User, 1);
console.log(user); // User { age: 1 }
console.log(user.age); // 1(返回值被忽略)
3.4 原型方法测试
function User(name) {
this.name = name;
}
User.prototype.sayHello = function() {
console.log(`Hello, I'm ${this.name}`);
};
const user = myNew(User, '愚公上岸说');
user.sayHello(); // Hello, I'm 愚公上岸说
3.5 复杂测试
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.introduce = function() {
return `I'm ${this.name}, ${this.age} years old`;
};
const person = myNew(Person, '愚公上岸说', 30);
console.log(person.name); // 愚公上岸说
console.log(person.age); // 30
console.log(person.introduce()); // I'm 愚公上岸说, 30 years old
console.log(person instanceof Person); // true
四、与原生 new 的对比
4.1 行为一致性
function User(name) {
this.name = name;
}
// 原生 new
const user1 = new User('愚公上岸说');
// 我们的实现
const user2 = myNew(User, '愚公上岸说');
console.log(user1.name === user2.name); // true
console.log(user1 instanceof User === user2 instanceof User); // true
4.2 边界情况
返回 null
function User() {
return null;
}
const user = myNew(User);
console.log(user); // {}(null 被忽略)
返回 undefined
function User() {
return undefined;
}
const user = myNew(User);
console.log(user); // {}(undefined 被忽略)
无返回值
function User(name) {
this.name = name;
// 没有 return
}
const user = myNew(User, '愚公上岸说');
console.log(user); // User { name: '愚公上岸说' }
五、实际应用场景
5.1 创建对象实例
function Car(brand, model) {
this.brand = brand;
this.model = model;
}
Car.prototype.start = function() {
console.log(`${this.brand} ${this.model} is starting...`);
};
const car = myNew(Car, 'Toyota', 'Camry');
car.start(); // Toyota Camry is starting...
5.2 继承中的应用
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(`${this.name} makes a sound`);
};
function Dog(name, breed) {
Animal.call(this, name);
this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
const dog = myNew(Dog, 'Buddy', 'Golden Retriever');
dog.speak(); // Buddy makes a sound
六、常见错误
6.1 忘记设置原型链
// ❌ 错误实现
function myNew(fn, ...args) {
let obj = {}; // 没有设置原型链
const res = fn.apply(obj, args);
return res instanceof Object ? res : obj;
}
// ✅ 正确实现
function myNew(fn, ...args) {
let obj = Object.create(fn.prototype); // 设置原型链
const res = fn.apply(obj, args);
return res instanceof Object ? res : obj;
}
6.2 返回值处理错误
// ❌ 错误实现
function myNew(fn, ...args) {
let obj = Object.create(fn.prototype);
const res = fn.apply(obj, args);
return res; // 没有处理基本类型返回值
}
// ✅ 正确实现
function myNew(fn, ...args) {
let obj = Object.create(fn.prototype);
const res = fn.apply(obj, args);
return res instanceof Object ? res : obj; // 正确处理返回值
}
七、面试要点总结
核心知识点
- new 的执行过程:创建对象 → 设置原型链 → 执行构造函数 → 返回对象
- 原型链设置:使用
Object.create设置__proto__ - this 绑定:使用
apply将this指向新对象 - 返回值处理:对象返回对象,基本类型返回新对象
常见面试题
Q1: new 的执行过程是什么?
答:
- 创建一个空对象
- 将对象的
__proto__指向构造函数的prototype - 将构造函数的
this指向新对象,并执行构造函数 - 如果构造函数返回对象,返回该对象;否则返回新对象
Q2: 如何实现 new?
答:使用 Object.create 创建对象并设置原型链,使用 apply 执行构造函数,根据返回值类型决定返回哪个对象。
Q3: 构造函数返回基本类型会怎样?
答:返回基本类型会被忽略,仍然返回新创建的对象。只有返回对象时才会使用返回值。
实战建议
- ✅ 理解 new 的执行机制
- ✅ 掌握原型链的设置方式
- ✅ 注意返回值的处理
- ✅ 能够手写实现 new
前端面试小册 文章被收录于专栏
每天更新3-4节,持续更新中... 目标:50天学完,上岸银行总行!
查看9道真题和解析