JS-new 操作符

42 阅读3分钟

前言

在 JavaScript 面向对象编程中,new 关键字是实例化对象的核心。面试官常常通过“手写 new”来考察你对原型链this 绑定以及构造函数返回值的理解。本文将带你从原理到实现,彻底搞懂 new 背后的魔法。

一、 new 到底干了什么?

当我们使用 new Person() 时,JS 引擎在背后默默执行了以下 4 个步骤

  1. 创建一个新对象:在内存中创建一个新的空对象(例如 obj = {})。

  2. 链接原型:将新对象的 __proto__ 属性指向构造函数的 prototype,从而实现原型继承(让实例能访问原型上的方法)。

  3. 绑定 this:将构造函数内部的 this 绑定到这个新对象上,并执行构造函数(为新对象添加属性)。

  4. 返回对象

    • 如果构造函数显式返回了一个对象(或函数),则返回该结果。
    • 如果构造函数没有返回对象(返回基本类型或无返回值),则返回步骤 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:如果构造函数返回 nullnew 出来的结果是什么?

参考回答: 结果是新创建的实例对象。 因为 typeof null === 'object',但 null 是个特殊值。在 new 的规范中,如果返回的是对象类型但值为 null,仍然会忽略它,返回实例对象。这就是为什么在手写代码中我们要判断 result !== null


结语

手写 new 是前端基础能力的试金石。理解了这 4 个步骤,你不仅能轻松应对面试,还能更深刻地理解 JavaScript 的继承机制。

如果你觉得这篇笔记对你有帮助,欢迎点赞收藏! 🚀