JavaScript學習3:原型和繼承

連江偉發表於2015-03-31

        原型

        我們建立的每一個函式都有一個prototype(原型)屬性,這個屬性是一個物件,它的用途是包含可以由特定型別的所有例項共享的屬性和方法。邏輯上可以這麼理解:prototype是通過呼叫建構函式而建立的那個物件的原型物件。

        為什麼要引入原型的概念呢?使用原型的目的,也是他的好處是可以讓所有的物件例項共享它所包含的屬性和方法。換句話說,就是不必再建構函式中定義物件資訊,而是可以直接將這些資訊新增到原型中。

        具體怎麼用,我們來看程式碼例項:   

<span style="font-size:18px;">//原型例項
function Person(){}   //宣告一個建構函式

Person.prototype.name='Lian';   //在原型裡新增屬性
Person.prototype.age=100;
Person.prototype.run=function(){    //在原型裡新增方法
	return this.name + this.age + '奮鬥中……';
};

var person1=new Person();
var person2=new Person();
alert(person1.run==person2.run);   //返回true,說明方法的引用地址是一致的,即兩個物件共享了一個方法</span>

        為了更好的理解建構函式的宣告方式和原型模式的宣告方式的區別,我找了兩張圖分享給大家,幫助大家理解:

        

        我們從圖中可以看到,在原型模式宣告中,多了兩個屬性,這兩個屬性都是建立物件時自動生成的。_proto_屬性是例項指向原型物件的一個指標,它的作用就是指向建構函式的原型屬性constructor。通過這兩個屬性,就可以訪問到原型裡的屬相和方法了。

        看到這裡你會覺得奇怪,上面程式碼例項中的建構函式的函式體中什麼也沒有,才能訪問到原型物件裡的值,如果函式體中有屬性或者方法呢?這裡就要涉及一個原型模式執行流程的問題了:是先去查詢建構函式例項裡面的屬性和方法,如果有,立刻返回,若沒有,則去它的原型物件中找,若有,則返回。

        使用原型模式建立物件也有其缺點,那就是它省略了建構函式傳參初始化這一過程,帶來的缺點就是初始化的值都是一樣的,但是這恰恰是它最大的優點,那就是共享。

        繼承

        繼承是物件導向中的一個核心概念,在比較正統的物件導向的語言中一般都會採用兩種方式實現繼承:一個是介面實現,一個是類繼承。而我們的JavaScript只支援繼承,而不支援介面實現,繼承是如何實現的,這裡要引入原型鏈的概念了。什麼是原形鏈,我們看一段程式碼就會知道。

<span style="font-size:18px;">//繼承例項
function A(){
	this.name ='Lian';
}

function B(){
	this.age=100;
}

function C(){
	this.address='中國';
}
B.prototype.age =200;
B.prototype =new A();  //B繼承了A

C.prototype =new B();  //C又繼承了B

var c=new C();
alert(c.name+' '+ c.age);  //C具有了A和B的屬性</span>

        在JavaScript中,被繼承的函式稱為超型別(也就是物件導向中說的父類或者說是基類),繼承的函式稱為子型別(即子類或者派生類)。繼承有好處,但是也有其自己的問題,比如字面量重寫原型會中斷關係,使用引用型別的原型,並且子型別無法給超型別傳遞引數。

        綜合考慮,我們使用原形鏈加上建構函式,這樣產生了組合繼承。      

<span style="font-size:18px;">//組合繼承
function Box(age){
	this.name='Lee';
	this.age=age;
}

Box.prototype.run=function(){
	return this.name +this.age;
};

function Desk(age ){
	Box.call(this,age);   //物件冒充,給超型別傳參
}

Desk.prototype =new Box();   //原形鏈繼承

var desk =new Desk(100);
alert (desk.run());   //顯然Desk繼承了Box的run方法</span>

        組合繼承是JavaScript最常用的繼承方式,但是,組合繼承也有一點問題,那就是超型別在使用過程中會被呼叫兩次,一次是建立子型別的時候,一次是在子型別建構函式的內部。這樣會帶來效能上的開銷,如何解決?留給讀者去思考……

相關文章