【前端面试小册】JS-第22节:实现 new

一、new 的概述

1.1 new 的作用

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。

1.2 new 的过程

当使用 new 调用构造函数时,会执行以下步骤:

  1. 创建一个空对象
  2. 将空对象的 __proto__ 指向构造函数的 prototype
  3. 将构造函数的 this 指向这个新对象,并执行构造函数
  4. 返回新对象(如果构造函数返回对象,则返回该对象;否则返回新创建的对象)

翻译过来就是我们需要

  1. 创建一个空对象 let obj = {}
  2. obj.__proto__ 指向构造函数 .prototype
    • 原型链作用:当对象上没有的属性会顺着 obj.__proto__ 去找
  3. 改变 this 指向 obj,并将参数传给构造函数
  4. 返回 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 Objectfalse,所以 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;  // 正确处理返回值
}

七、面试要点总结

核心知识点

  1. new 的执行过程:创建对象 → 设置原型链 → 执行构造函数 → 返回对象
  2. 原型链设置:使用 Object.create 设置 __proto__
  3. this 绑定:使用 applythis 指向新对象
  4. 返回值处理:对象返回对象,基本类型返回新对象

常见面试题

Q1: new 的执行过程是什么?

答:

  1. 创建一个空对象
  2. 将对象的 __proto__ 指向构造函数的 prototype
  3. 将构造函数的 this 指向新对象,并执行构造函数
  4. 如果构造函数返回对象,返回该对象;否则返回新对象

Q2: 如何实现 new?

答:使用 Object.create 创建对象并设置原型链,使用 apply 执行构造函数,根据返回值类型决定返回哪个对象。

Q3: 构造函数返回基本类型会怎样?

答:返回基本类型会被忽略,仍然返回新创建的对象。只有返回对象时才会使用返回值。

实战建议

  • ✅ 理解 new 的执行机制
  • ✅ 掌握原型链的设置方式
  • ✅ 注意返回值的处理
  • ✅ 能够手写实现 new
#前端面试##前端#
前端面试小册 文章被收录于专栏

每天更新3-4节,持续更新中... 目标:50天学完,上岸银行总行!

全部评论

相关推荐

11月了,甚至没有一个流程中的😅不知道是不是北森没认真写还是笔试a太少了,九月投到现在甚至只有两个面试。虎牙一面挂得不明不白,字节二面挂,现在看起来滴滴转正也是没戏了的。广东人不想离开广东,想着广州深圳这么大怎么着也能找个养活自己,但是实际情况是,腾讯暑期到现在就没被捞过一次,广东这边字节投了也没反应。中小厂也基本没反应,零一空间面完让我写笔试题,也挂的不明不白,感觉回答也七七八八,笔试也基本写。现在是看着周围的人上岸中大厂,每一天压力是真的大,高考确实我也尽力了,遇上22年广东高考数学爆炸直接平时还能看得上眼的也没了。为了弥补学历差距,我也觉得我努力了,大二暑假开始实习,从小厂到中厂到大厂,我也努力拼了。回想起滴滴刚oc时候的觉得一切都好起来了,怀着激动的心独自坐上前往北京的飞机,原来也只是昙花一现,来这里才发现是误闯天家,确实也学到了很多,但是也明白确实cover不了正职的活,可能真是学习能力不比了92,写文档和组织代码能力还在被mt喷中😭有时候真在想是不是没有这一年的实习,在学校混混日子,毕业了跑滴滴送外卖也心安理得,但是卷过了,是真不甘心了。好在十月和ld申请转base回到了广州继续实习,很难想象在北京我现在会是什么状态。要不是房租还在交着,真想辞了回家睡几天,虽然也没啥颜面回去面对家人的期望。现在还有两个据说有转正hc的实习生面试,虽然也可能是画大饼,但是属于是转正也比不上同学提前实习工资那种。未来会好起来吗?不知道。春招会好起来吗?我觉得难说。想起之前自信得说我一定能找到满意的工作,很想笑了。现在想起来,日常实习的时候真的是最幸福的时光,没有压力,学得到东西,自己赚钱花也心安理得。
无面如何呢:会赢的
点赞 评论 收藏
分享
查看23道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务