以前寫過一篇Javascript繼承主題的文章,這篇文章做爲一篇讀書筆記,分析的不夠深刻。html
本文試圖進一步思考,爭取完全理解Javascript繼承原理函數
舉一個《高性能Javascript》書中例子性能
var book={ title :"High Performance JavaScript", publisher:"Yahoo!Press" }; alert(book.toString());//"[object Object]"
Javascript對象有兩種類型的成員:實例成員和原型成員。實例成員存在於實例自身,原型成員則從對象的原型繼承this
上述示例中,book對象有兩個實例成員:title和publisher。book對象並無定義toString()方法,可是卻能夠調用這一方法,這是由於toString()方法是book對象繼承的一個原型成員spa
book示例成員與原型成員關係以下圖。當book.toString()被調用時,首先從對象實例搜索toString方法,若是沒有找到,就轉向搜索原型對象。經過這種方式,book能夠訪問他的原型全部的屬性和方法。prototype
function Person(){} Person.prototype.name="Nicholas"; Person.prototype.age=29; Person.prototype.job="Software Engineer"; Person.prototype.sayName=function(){alert(this.name)}; var person1=new Person(); person1.sayName();//"Nicholas" var person2=new Person(); person2.sayName();//"Nicholas"
上述代碼中,Person構造函數、原型對象和實例對象見關係以下設計
將Person屬性成員再也不定義在原型對象上時code
function Person(name) { this.name=name; } Person.prototype.sayName=function(){alert(this.name)}; var p1=new Person("Zhangsan");
上述代碼中,Person構造函數、原型對象和實例對象關係以下圖所示orm
從上面兩圖能夠看出,當屬性定義在構造函數中時,屬性成員存在於實例對象中;屬性定義在原型對象中時,實例對象訪問這一屬性,須要沿原型鏈查找該屬性htm
讓構造函數的原型對象等於另外一個類型的實例,利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法
function SuperType() { this.property=true; } SuperType.prototype.getSuperValue=function(){ return this.property; }; function SubType() { this.subProperty=false; } //繼承SuperType SubType.prototype=new SuperType(); SubType.prototype.getSubValue=function(){ return this.subProperty; } var instance=new SubType(); alert(instance.getSuperValue());//true
代碼示例中,完整原型鏈以下
圖中值得注意的細節是,父類SuperType的屬性property存在於SuperType的實例對象中,getSuperValue方法存在於其原型對象中
將SuperType實例對象賦給子類SubType的原型對象後,SubType原型對象prototype得到了父類的property屬性
上文是經過將父類的實例對象賦給子類的原型對象,實現了繼承的效果
直接將父類的原型對象賦給子類的原型對象行不行?
function Super() { this.attr='a'; } Super.prototype.func=function(){alert('Super');}; function Sub() { this.x='1'; } Sub.prototype =Super.prototype; Sub.prototype.test=function(){alert('Sub');} var sub=new Sub(); sub.func();//'Super' sub.test();//'Sub' sub.attr;//undefined sub.x;//1
上述代碼中,將Super.prototype直接賦給Sub.prototpye,發現對Sub類型實例對象sub,訪問attr屬性時,沒法獲取
這就是由於attr屬性定義在Super構造函數中,而不是定義在Super的原型對象上,此時Super.prototype中並無attr屬性
假設將Super全部屬性都定義在原型對象上,以下所示,這時sub實例對象是能夠訪問到父類的attr屬性的
但這時,Super類全部實例對象,都共享了attr屬性,一個實例對attr屬性的更改,會引發其餘實例對象attr屬性值的變動,這是應該避免的
function Super() { } Super.prototype.attr='a'; Super.prototype.func=function(){alert('Super');}; function Sub() { this.x='1'; } Sub.prototype =Super.prototype; Sub.prototype.test=function(){alert('Sub');} var sub=new Sub(); sub.attr;//a sub.x;//1
function Super() { this.attr='a'; } Super.prototype.func=function(){alert('Super');}; function Sub() { this.x='1'; } Sub.prototype =Super(); Sub.prototype.test=function(){alert('Sub');} var sub=new Sub(); sub.func(); sub.test(); sub.attr; sub.x;
實際上,上面代碼會直接報錯,提示test屬性沒有被定義
緣由是Sub.prototype=Super();這句代碼,其實是將Sub.prototype指向了undefined,由於Super()語句,是在以函數行駛調用Super()方法,這一調用,是沒有返回值的
通過這一調用,Sub.prototype此時已經爲undefined,再爲他定義test屬性,天然就報錯了