在了解javaScript的原型链之前,我们得先来看一下原型是什么。
在javaScript中,所有的函数都会有着一个特别属性:prototype(显示原型属性);当我们运行如下代码时:
function Fn(){ }
在内存中则是如下这样工作的:
我们可以看到,当function对象创建时,其会有一个属性:prototype,该属性在定义函数的时候被自动赋值,值默认为:{},即:指向堆空间中的空的object对象,而该object空对象即是此function的原型对象。
在函数实例化对象时,所有的实例化对象也会有一个特别的属性:__proto__(隐式原型属性),继续刚才的例子,我们运行如下代码:
function Fn(){ } var fn=new Fn();
此时内存中是如下状况:
此时有没有发现function对象的显示原型属性与其实例化得到的对象的隐式原型属性的值是相同的,即它们指向同一个对象——空object(function的原型对象)。
下面我们来说下显示原型属性与隐式原型属性的关系。
在function实例化对象时,在其实例化对象内部相当于自动运行了如下代码:
var fn=new Fn(); //fn内部:this.__ptoto__=Fn.prototype
我们可以通过如下代码来判断显示原型属性与隐式原型属性是否相等:
function Fn(){ } var fn=new Fn(); //fn内部:this.__ptoto__=Fn.prototype console.log(fn.__proto__===Fn.prototype);
运行上述代码会输出:true,这说明显示原型属性与隐式原型属性确实相等。
由此我们可以总结:函数实例化对象的隐式原型属性等于其构造函数的显示原型属性。
了解过原型之后,我们来看一下原型链。
先来看一张图:
那么这个空的object对象是不是一个实例化对象呢,如果是的话,它的构造函数又是谁呢?
在回答上述问题之前我们先来看一个新的属性:constructor;
这个属性与prototype作用类似,不过不同的是:prototype是function对象指向它对应的原型对象,而constructor则是原型对象指向其对应的function对象。
举个例子:
function Fn(){ } var fn=new Fn(); //fn内部:this.__ptoto__=Fn.prototype console.log(fn.__proto__.constructor);
若我们运行上述代码,则会得到如下结果:
通过这个例子我们可以知道:Fn的实例化对象fn可以通过fn.__proto__.constructor来得到自身的构造函数。
它的原理也很简单,由前面我们可以知道,fn.__proto__===Fn.prototype是成立的,所以可以通过fn.__ptoto__来得到Fn的原型对象,然后再使用constructor来得到与该原型相对应的function函数,即:Fn。
再回到前面的问题上,我们同样可以通过上述方法来获得object的构造函数。
即执行:
function Fn(){ } var fn=new Fn(); //fn内部:this.__ptoto__=Fn.prototype console.log(Fn.prototype.__proto__.constructor);
其输出结果如下:
我们可以看到,object空对象的构造函数是Object。
那么问题又来了,我们知道:函数实例化对象的隐式原型属性等于其构造函数的显示原型属性。
这个object空对象的构造函数既然是Object,那么object应该也通过__ptoto__来指向一个原型对象。
即:
那么Object函数原型对象的隐式原型又指向谁呢?
我们来看一下:
function Fn(){ } var fn=new Fn(); //fn内部:this.__ptoto__=Fn.prototype console.log("输出结果为:"+Fn.prototype.__proto__.__proto__);
由此可以知道:Object函数原型对象的隐式原型不再指向其他对象,即:
当我们运行如下代码时:
function Fn(){ } var fn=new Fn(); //fn内部:this.__ptoto__=Fn.prototype Object.prototype.name='mmzkyl'; console.log("输出结果为:"+fn.name);
会得到如下输出:
我们可以很明显看到,上述代码并没有在Fn内部定义任何东西,但是fn.name确实又输出了:mmzkyl。
这里来解释一下,当查找对象内部属性的属性/方法时,JavaScript引擎会自动沿着如下图所示来进行查找:
JavaScript会先在自身查找目标属性/方法,若是没有查找到则会沿着其隐式原型属性渠道原型对象中寻找,若是找到了则返回该值,若是还没有找到,则继续沿着隐式原型属性寻找,若是一直找到了Object函数原型对象还是没有找到,则会返回undefined。
这个寻找的过程类似与一条链,而这也称为原型链。