自己重新手写了一遍new

154 阅读2分钟

在红皮书《JavaScript高级程序设计》中有这么一段话描述了new操作符:

要创建一个对象的新实例,必须使用new操作符,以这种方式调用构造函数实际上会经历以下4个步骤:

1、创建一个新对象;

2、将构造函数的作用域赋给新对象(因此this就指向了这个对象);

3、执行构造函数中的代码(为这个新对象添加属性);

4、如果构造函数返回对象,则返回该对象;否则,返回刚创建的新对象(空对象)。

由于对象的构造函数体的不同,也分为多种情况:

  1. 无返回值
   function Children(name, age) {
       this.name = name;
       this.age = age;
   }
   console.log(new Children('xiaoming', 3)); //{name: 'xiaoming', age: 3}
  1. 返回值是一个对象
    function Children(age) {
        this.age = age;
        return {name: 'xiaohong', age: 4};
    }
    console.log(new Children(3)) // {name: 'xiaohong', age: 4}
  1. 返回值是非对象
    function Children(name, age){
        this.name = name;
        this.age = age;
        return "奥特曼";
    }
    console.log(new Children('小白', 5)) // {name: '小白', age: 5}
  1. 返回值是非对象且没有属性绑定
    function Children(name, age) {
        return "怪兽";
    }
    console.log(new Children('xiaoqiang', 6)); // {}

从上面几种情况可以看出,只有构造函数的return返回的是一个对象类型时,才会改变结果

众所周知,只有function类型的数据才会被实例化,class也不例外,但是 Generator 函数和箭头函数却不能

    function* gen() {
      yield 'hello';
      yield 'world';
      return 'ending';
    }
    const arr = () => {
        console.log(arr);
    }
    function common(age, name){
        this.age = age;
        this.name = name;
    }
    class Point {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
    }
    console.log(typeof gen); // fuinction
    console.log(typeof arr); // fuinction
    console.log(typeof common); // fuinction
    console.log(typeof Point); // fuinction

箭头函数是因为没有this指向而无法实例化new,而Generator 函数返回的总是遍历器对象,而不是this对象,所以在手写自己的new函数时要把这两种函数排至在外。

以下是完整代码

    function _new(context, ...args) {

        // 判断构造函数的类型合法

        if(typeof context !== 'function') {

            throw new Error('parameter type error!')

        }

        // 针对箭头函数

        if(!context.hasOwnProperty('prototype')) {

            throw new Error(`${context.name} is not a constructor`)

        }

        // 针对generator函数

        if(context.constructor.name === 'GeneratorFunction'){

            throw new Error(`${context.name} is not a constructor`)

        }

        // 创建一个空对象

        let obj = {};

        // 将构造函数的作用域赋给新对象

        obj.__proto_ = context.prototype;

        // 执行构造函数中的代码

        const res = context.apply(obj, args);

        // 如果有返回值且返回值是对象,则将它作为返回值,否则返回之前创建的新对象

        const isObject = typeof res === 'object' && res !== null;

        const isFunction = typeof res === 'function';

        return isObject || isFunction ? res : obj;

    }