完全理解Javascript原型繼承

完全理解Javascript原型繼承

 以前寫過一篇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屬性,天然就報錯了

相關文章
相關標籤/搜索