深入理解new

阿凡达2018-07-05 18:39


本文讲详细介绍Javascript中new操作背后的故事。

引子

  假设我们有个Person构造函数,并且使用new操作实例化了一个jerry对象,示例如下:

    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    Person.prototype.introduce = function() {
        return "I am " + this.name + ", " + this.age + " years old.";
    };
    var jerry = new Person("jerry", 1);
    jerry.introduce();  // "I am jerry, 1 years old."

  在Chrome DevTools中,我们可以看到jerry的内部结构,如下:

  jerry对象有name/age两个属性,还有个__proto__属性指向jerry对象的原型(也就是Person.prototype)。__proto__是引擎沿原型链查找属性时使用的,程序一般不会使用。
  jerry对象是如何一步步构造出来的了?构造一个对象,首先肯定要分配一块内存空间来存放这个对象, 我们还知道在构造函数执行的时候添加了name/age属性, 那么什么时候添加了__proto__呢?我们在Person函数的起始位置设上断点,运行到断点时对象的结构如下:

  我们发现在构造函数运行以前__proto__属性已经指向了Person.prototype

自定义new

  结合上文案例的分析,我们可以用一个函数来模拟new操作,达到与new操作相同的效果,代码如下:

    // 模拟new操作
    function myNew(cls) {
        if (typeof cls !== 'function') return;
        // 创建一个以构造函数prorotype属性为原型的对象
        var _this = Object.create(cls.prototype);
        // 调用构造函数
        var obj = cls.apply(_this, [].slice.call(arguments, 1));
        return (typeof obj === 'object'||typeof obj === 'function') ? obj : _this;
    }
    // 使用自定义的new方法实例化对象
    var jerry = myNew(Person, 'jerry', 1);
    jerry.introduce();  // "I am jerry, 1 years old."

  我们需要注意一下,如果构造函数返回结果为对象(function也是对象类型,详情请参见Javascript数据类型之基本数据类型),则new操作就返回这个对象,否则才会返回this(参见myNew方法最后一句)。Object.create方法的作用是创建一个指定原型的对象,详情请参见mdn Object.create

总结

  在构造函数的使用中,我们不推荐构造函数返回对象,应该让它默认返回this。通常情况下,Javascript中new操作符构造对象的过程如下:

分配一块内存空间,将this指向这块空间

this.__proto__属性指向构造函数的.prorotype属性

this为上下文调用构造函数

返回this

本文来自网易实践者社区,经作者魏文庆授权发布。