本文讲详细介绍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操作
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
本文来自网易实践者社区,经作者魏文庆授权发布。