在真正去实现new的时候,我们先来关注一下,new操作符的功能都有那些?只有真正懂得来new操作符的功能,我们才能自己去实现一下new操作符。


new到底做了哪些事情

我们先写一段很简单的代码,定义一个Person类, 使用new来创建一个Person的实例.

  1. function Person(firtName, lastName) {
  2. this.firtName = firtName;
  3. this.lastName = lastName;
  4. }
  5. Person.prototype.getFullName = function () {
  6. return `${this.firtName} ${this.lastName}`;
  7. };
  8. const tb = new Person('Chen', 'Tianbao');
  9. console.log(tb);

查看一个控制台中tb实例的.
new 操作符的实现 - 图1
从图中我们可以看到.实例里面有以下东西:

  • 两个属性, firtName和lastName, 并均以赋值.
  • 原型上有一个getFullName方法和一个构造器.

    分析完实例后, 我们就很容易知道, new到底做了什么.

  • 创建一个新的对象

  • 添加父类的属性到新的对象上并初始化.
  • 继承父类原型上的方法.
  • 返回新对象. 但是? 上面的描述完全正确吗?

我将我们的demo代码, 做一点点更改. 在构造器上添加一个return.

  1. function Person(firtName, lastName) {
  2. this.firtName = firtName;
  3. this.lastName = lastName;
  4. return {
  5. fullName: `${this.firtName} ${this.lastName}`
  6. };
  7. }

控制台中,看看有什么不一样.
new 操作符的实现 - 图2
我们发现, 这是执行后, 实际的实例, 返回的就是一个普通的object对象. 这个对象就是执行return时的结果.
我们进一步探索, 如果返回的不是一个对象, 而是一个Nubmer和String, 会怎么样呢?

  1. function Person(firtName, lastName) {
  2. this.firtName = firtName;
  3. this.lastName = lastName;
  4. return 'demo';
  5. }

从控制台中, 可以看到, 和没有写return是一样的. 返回的都是新创建的Person实例.
new 操作符的实现 - 图3

经过上面的分析, new到底做了什么事情, 我们就很容易归纳了.

  • 创建一个新的对象
  • 继承父类原型上的方法.
  • 添加父类的属性到新的对象上并初始化. 保存方法的执行结果.
  • 如果执行结果有返回值并且是一个对象, 返回执行的结果, 否则, 返回新创建的对象。

    MDN 中对 new 运算符的解释 当代码 newFoo(…) 执行时,会发生以下事情:

    1. 一个继承自 Foo.prototype 的新对象被创建。
    2. 使用指定的参数调用构造函数 Foo,并将 this 绑定到新创建的对象。newFoo 等同于new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。
    3. 由构造函数返回的对象就是new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)

知道执行的过程后, 如何手写一个new方法. 就变的很容易了.

代码如下:

  1. /**
  2. * 手写源码: new关键字实现
  3. * @param {object} target 目标对象
  4. * @param {...any} rest 剩余参数
  5. * @returns 新的对象
  6. */
  7. function _new(target, ...rest) {
  8. // 基于目标对象的原型创建一个新的对象
  9. let newObj = Object.create(target.prototype);
  10. // 添加属性到新创建的newObj上, 并获取target函数执行的结果.
  11. const result = target.call(newObj, ...rest);
  12. // 如果执行结果有返回值并且是一个对象, 返回执行的结果, 否则, 返回新创建的对象
  13. return typeof result === 'object' ? result : newObj;
  14. }

测试一下.

  1. const tb = new Person('Chen', 'Tianbao');
  2. console.log(tb);
  3. const tb2 = _new(Person, 'Chen', 'Tianbao');
  4. console.log(tb2);

我们发现tb和tb2的返回值, 完全一样new 操作符的实现 - 图4

参考文章(拷贝文章)

如何手写一个JS中的New方法
new 运算符MDN