原型链
简单回顾一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么,假如让原型对象等于另一个类型的实例,结果会怎样?此时的原型对象将包含一个指向另一个原型 的指针,相应的,另一个原型中也包含着一个指向另一个构造函数的指针,如此层层递进,就形成了原型链。
接下来我们将一步一步理解原型,逐步深入直至理解原型链。
1、构造函数创建实例对象
function SuperType() {
}
var instanceSuper = new SuperType();
在这个例子中,我们用new创建了一个实例,那么new具体做了什么呢?MDN上是这么说的,对于var o = new Foo();
所以当我们执行new SuperType()的时候,js会这样做
1、首先创建一个对象:var o = new Object();
2、将o对象的原型指向构造函数的原型:o.__proto = SuperType.prototype;
3、将this对象指向o:Supertype.call(o);
4、返回o对象给instanceSuper,完成var instanceSuper = new SuperType();这个过程
接下来进入正题
2、prototype
每个函数都有一个prototype属性,注意是每个函数,这也是我们经常用到的一个属性,还是上面那个例子,我们来修改一下
function SuperType() {
}
SuperType.prototype.getSuperValue = function() {};
var instanceSuper = new SuperType();
那这个函数的prototype属性到底指向了什么呢?prototype属性指向一个对象,这个对象就是使用该构造函数创建的实例的原型,即instanceSuper的原型,要记住JS中一切皆对象。
那什么是原型呢?可以这样理解,每个JS对象(除null)外在创建的时候就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型“继承”属性
接下来看一下实例和原型之间的关系,这又涉及到一个属性: __proto__
3、__proto__
每个对象(除了null)都有一个__proto__对象,这个属性指向该对象的原型。还是前面那个例子
instanceSuper.__protp__ === SuperType.prototype // true
也就是构造函数和实例都可以指向原型
注:__proto__是在查找链中用来解析方法的实际对象,prototype是在用new创建对象时用来构建__proto__的对象
4. constructor
每个原型都有一个constructor属性指向关联的构造函数
SuperType === Supertype.prototype.contructor
明白了这几个关键词,我们就可以更好的理解原型链。
来看一种实现原型链的基本模式:
function SuperType() {
}
SuperType.prototype.getSuperValue = function() {};
function SubType() {}
SubType.prototype = new SuperType();
var instanceSuper = new SubType();
SubType没有用默认原型,而是赋予了一个新原型——SuperType的实例,所以subType的原型没有构造函数,如果需要构造函数可以显示指定,如:SubType.prototype.construtor = SubType;
instanceSuper指向SubType的原型,SubType的原型又指向SuperType的原型,所以函数的默认原型都是Object的实例,默认原型都有一个内部指针,指向Object.prototype。这也是所有自定义类型都会继承toString()、valueOf()等默认方法的根本原因。而Object的原型就是null。
查找实例属性时,先查找实例本身,如果没有,通过__ptoto__向上查找原型,如果找到就返回结果,否则继续向上查找,直到Object的原型,找不到就结束。