以前对js中的原型链和原型对象有所了解,每当别人问我什么是原型链和原型对象时,我老是用很官方(其实本身不懂)的解释去描述。有一句话说的好:若是你不能把一个很复杂的东西用最简单的话语描述出来,那就说明你没有真正的理解。最近正在读《Javascript高级程序设计》,书中对原型对象和原型链的描述让我受益不浅,下面仅用一个对比性的例子来讲明。javascript
function Person () { this.name = 'John'; } var person = new Person(); Person.prototype.say = function() { console.log('Hello,' + this.name); }; person.say();//Hello,John
上述代码很是简单,Person原型对象定义了公共的say方法,虽然此举在构造实例以后出现,但由于原型方法在调用以前已经声明,所以以后的每一个实例将都拥有该方法。从这个简单的例子里,咱们能够得出:原型对象的用途是为每一个实例对象存储共享的方法和属性,它仅仅是一个普通对象而已。而且全部的实例是共享同一个原型对象,所以有别于实例方法或属性,原型对象仅有一份。
全部就会有以下等式成立:java
person.say == new Person().say
function Person () { this.name = 'John'; } var person = new Person(); Person.prototype = { say: function() { console.log('Hello,' + this.name); } }; person.say();//person.say is not a function
很不幸,person.say方法没有找到,因此报错了。其实这样写的初衷是好的:由于若是想在原型对象上添加更多的属性和方法,咱们不得不每次都要写一行Person.prototype,还不如提炼成一个Object来的直接。可是此例子巧就巧在构造实例对象操做是在添加原型方法以前,这样就会形成一个问题:
当var person = new Person()
时,Person.prototype为:Person {}
(固然了,内部还有constructor属性),即Person.prototype指向一个空的对象{}。而对于实例person而言,其内部有一个原型链指针proto,该指针指向了Person.prototype指向的对象,即{}。接下来重置了Person的原型对象,使其指向了另一个对象,即Object {say: function}
,
这时person.proto的指向仍是没有变,它指向的{}对象里面是没有say方法的,由于报错。
从这个现象咱们能够得出:在js中,对象在调用一个方法时会首先在自身里寻找是否有该方法,若没有,则去原型链上去寻找,依次层层递进,这里的原型链就是实例对象的__proto__属性
。函数
若想让上述例子成功运行,最简单有效的方法就是交换构造对象和重置原型对象的顺序,即:ui
function Person () { this.name = 'John'; } Person.prototype = { say: function() { console.log('Hello,' + this.name); } }; var person = new Person(); person.say();//person.say is not a function
其实,只须要明白原型对象的结构便可:this
Function.prototype = { constructor : Function, __proto__ : parent prototype, some prototype properties: ... };
总结:函数的原型对象constructor默认指向函数自己,原型对象除了有原型属性外,为了实现继承,还有一个原型链指针__proto__,该指针指向上一层的原型对象,而上一层的原型对象的结构依然相似,这样利用__proto__一直指向Object的原型对象上,而Object的原型对象用Object.__proto__ = null表示原型链的最顶端,如此变造成了javascript的原型链继承,同时也解释了为何全部的javascript对象都具备Object的基本方法。
spa