五行代码带你实现一个new操作符

115 阅读2分钟

在 JavaScript 中,new 操作符是用于创建一个对象实例的运算符。 使用 new 操作符调用一个构造函数,会自动执行以下四个步骤

  1. 创建一个空对象。
  2. 将新对象的原型指向构造函数的原型。
  3. 执行构造函数,并将 this 指向新对象。这里会得到一个返回值,命名为result
  4. 返回新对象(result)。

总之,我们想要实现一个new操作符,只要在函数中封装好这四步就可以了。

然而new操作符的实现有很多种方式。这里提供两种方式给大家做参考。

我们将在注释中使用(1)(2)(3)(4)来标注核心步骤。

初代目

function MyNew(func) {
      let obj = {}; //(1)创建一个空对象。
      if (func.prototype !== null) {
        //如果原型属性不存在,则不需要设置新对象的原型为构造函数的原型
        obj.__proto__ = func.prototype; //(2)设置原型
      }
      let result = func.apply(obj, Array.prototype.slice.call(arguments, 1)); 
      //(3)执行构造函数并获得result
      if (
        typeof result === "object" ||
        (typeof result === "function" && result !== null) //检查构造函数的返回值是否为对象或者函数
      ) {
        return result; //(4)返回新对象
      }
      return obj; //否则返回空对象
    }

arguments 对象是Javascript中的一个类数组对象,它包含了函数被调用时传入的所有参数。

Array.prototype.slice.call(arguments, 1) 的目的是将 arguments 对象(类数组对象)从第二个元素开始截取为一个真正的数组对象,并返回这个数组对象。

然而这段代码似乎有些太长了。不加注释阅读起来还是有些困难的。

Array.prototype.slice.call(arguments, 1)也有些让人不解。

我们可以将其写的更优雅一些。

二代目

function MyNew(func, ...args) {
  const obj = Object.create(func.prototype);//(1)(2)创建一个空对象并将新对象的原型指向构造函数的原型
  const result = func.apply(obj, args);//(3)执行构造函数并获得result
  return Object(result) === result ? result : obj; //(4)返回新对象
}

怎么样,这下是不是好懂多了呢?

Object.create() 方法是 JavaScript 中用于创建一个新对象,并将其原型对象指向另一个对象或者 null 的方法。

在这里相当于帮我们把(1)(2)步都完成了。

结果测试:

 function Person(name, age) {   //创建一个Person构造函数
      this.name = name;
      this.age = age;
    }
 console.log(MyNew(Person, "小明", 18));  

输出结果为:

QQ图片20230314004402.jpg

最后,事实上new操作符在实际中有更多的边界处理,但这都不是我们要了解的核心。

本文用于记录自己的学习过程,欢迎大家指出错误。