new
关键字会进行如下的操作:
- 创建一个空的简单JavaScript对象(即
{}
); - 为步骤1新创建的对象添加属性 proto ,将该属性链接至构造函数的原型对象 ;
- 将步骤1新创建的对象作为
this
的上下文 ; - 如果该函数没有返回对象,则返回
this
。
为什么要这么做呢? 无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 prototype 属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个 constructor (构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(proto),指向构造函数的原型对象。
上图展示了 Person 构造函数、Person 的原型属性以及 Person 现有的两个实例之间的关系。 在此,Person.prototype 指向了原型对象,而 Person.prototype.constructor 又指回了 Person。 原型对象中除了包含 constructor 属性之外,还包括后来添加的其他属性。Person 的每个实例—— person1 和 person2 都包含一个内部属性,该属性仅仅指向了 Person.prototype;换句话说,它们 与构造函数没有直接的关系。
用代码模拟实现:
function Mocknew() {
var obj = new Object();
//取出第一个参数,就是我们要传入的构造函数。shift 会修改原数组,所以 arguments 会被去除第一个参数
Constructor = Array.prototype.shift.call(arguments);
obj.__proto__ = Constructor.prototype;
Constructor.apply(obj, arguments);
return obj;
};
function Person(name, age){
this.name = name;
this.age = age
}
let p1 = Mocknew(Person,'zl',30)
注:为什么要用apply而不是bind呢,apply绑定this并执行该方法即构造函数,也就是让实例对象具有构造函数内的属性或方法,使用原型链的方式完美的实现了继承。当然,如果构造函数Person返回一个对象,那么在Mocknew方法内返回obj时需要判断一下,这里不做赘述,只是简单描述一下new的原理。