new方法的手写实现

263 阅读2分钟

使用

想要知道怎么实现一个new方法,我们先要看看new怎么使用,咱们直接上例子

    function Animal(type) {
        this.type = type;
    }
    Animal.prototype.say = function() {
        console.log('say');
    };
    let dog = new Animal('dog');
    console.log(dog);
    dog.say()

以上例子就简单的使用了new来创建一个dog实例 而且知道了:

  1. dog可以访问 Animal 构造函数里的属性
  2. dog可以访问 Animal.prototype 中的属性
  3. dog是一个实例对象

分析

我们来说下 构造函数-实例对象-原型对象 三者的关系

  1. 通过构造函数new得到实例对象
  2. 每个实例对象都有一个__proto__属性并且指向原型对象,
  3. 原型对象的constructor指向构造函数

new过程做了啥

  1. 创建了一个空对象
  2. 执行构造函数使得这个新创建的对象拥有构造函数的属性与方法
  3. 继承构造函数原型上的属性与方法
  4. 返回这个实例化后的对象

代码实现

  1. 有了上面的分析,我们就比较容易去实现一个自己的new方法了
    function mockNew() {
        let obj = {}; 
        //创建 一个新对象
        let constructor = [].shift().call(arguments); 
        //shift方法删除数组第一个元素并返回,shift是会改变数组的
        obj.__proto__= constructor.prototype;
        //实例的隐式原型指向构造函数原型
        constructor.apply(obj,arguments);
        //使用apply,改变构造函数this的指向新建的对象,这样obj就可以访问构造函数中的属性
        //也可以用call 那就的展开arguments参数了
        return obj;
        //返回该对象
    }
  1. 其实这个new还有一个特点,就是如果在构造函数里定义了返回值,会根据你定义的返回值不同的得到的实例结构也不一样
    1. 如果在构造函数里return了一个对象,那么返回值就是你定义的那个对象
    2. 如果在构造函数里没有定义return的值,会默认返回你所创建的对象
    3. 如果在构造函数里return的是非对象,而是基本类型的数据,那么自定义的return会被忽略,返回的还是我们创建的那个对象
    4. 这点需要注意
  2. 最终代码如下
    function mockNew() {
        let obj = {}; 
        //创建 一个新对象
        let constructor = [].shift().call(arguments); 
        //shift方法删除数组第一个元素并返回,shift是会改变数组的
        obj.__proto__= constructor.prototype;
        //实例的隐式原型指向构造函数原型
        const result = constructor.apply(obj,arguments);
        //使用apply,改变构造函数this的指向新建的对象,这样obj就可以访问构造函数中的属性
        //也可以用call 那就的展开arguments参数了
        return result instanceof Object ? result : obj;
        //返回该对象
    }

总结

以上是对new的简单理解与实现,如有问题还望大家指证,在评论区留言