深刻理解原型鏈

深刻理解原型鏈

1.原型鏈

原型鏈做爲實現繼承的主要方法:其基本思想是利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法。javascript

首先咱們得清楚構造函數(constructor),原型對象(prototype)和實例的三者關係。html

每個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。因此當咱們讓一個構造函數的原型對象等於另外一個類型的實例java

function A(){};
function B(){};
B.prototype=new A();
var instance=new B();

instance的原型對象是B.prototype,而B.prototype又等於構造函數A的一個實例對象,A的實例對象的原型對象是A.prototype,這樣就造成了一條原型鏈了。編程

實例:數組

function SuperType(){//定義一個SuperType類型
    this.property = true;//給實例對象添加一個屬性
}
        
SuperType.prototype.getSuperValue = function(){//給原型對象添加一個方法
    return this.property;
};
        
function SubType(){//定義一個SubType類型
    this.subproperty = false;
}
        
//inherit from SuperType
SubType.prototype = new SuperType();//建立一個SuperType的實例對象,並把它賦值給SubType.prototype
        
SubType.prototype.getSubValue = function (){//給原型對象添加一個方法
    return this.subproperty;//返回實例屬性
};

var instance = new SubType();//建立一個對象實例
alert(instance.getSuperValue());   //true

建立一個SuperType的實例對象,並把它賦值給SubType.prototype,也就是說,原來存在於SuperType的實例對象中的全部屬性和方法,如今也存在於SubType.prototype中了,咱們在給SubType.prototype添加了一個方法,這樣就在繼承SuperType的屬性和方法的基礎上又添加了一個新方法。函數

咱們沒有使用SubType默認提供的原型,而是給它換了一個新原型;這個新原型就是SuperType的實例。因而,新原型不只具備做爲一個SuperType的實例所擁有的所有屬性和方法,並且其內部還有一個指針,指向了SuperType的原型。this

如今instance指向SubType的原型,Subtype的原型指向SuperType的原型。getSuperValue()方法仍然在SuperType.prototype中,但property則位於SubType.prototype中了。由於property是一個實例屬性,而getSuperValue()方法則是一個原型方法。既然SubType.prototype如今是SuperType的實例,那麼property固然就位於該實例中了。prototype

注意:

instance.constructor此時指向的是SuperType,這是由於原來的SubType.prototype被重寫的緣故。任何一個prototype對象都有一個constructor屬性,指向它的構造函數。若是沒有"SubType.prototype = new SuperType();"這一行,SubType.prototype.constructor是指向SubType的;加了這一行之後,SubType.prototype.constructor指向SuperType。更重要的是,每個實例也有一個constructor屬性,默認調用prototype對象的constructor屬性。所以,在運行"SubType.prototype = new SuperType();"這一行以後,instance.constructor也指向SuperType!這顯然會致使繼承鏈的紊亂(instance明明是用構造函數SubType生成的),若是咱們想要徹底符合繼承,能夠將將SuperType.prototype對象的constructor值改成SuperType。設計

2.謹慎地定義方法

經過原型實現繼承時,不能使用對象字面量建立原型方法,由於這樣會重寫原型鏈指針

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;
    },
        
    someOtherMethod : function (){
        return false;
    }
};
        
var instance = new SubType();
alert(instance.getSuperValue());   //error!

3.原型鏈的問題

原型鏈雖然很是強大,可是它也存在一些問題,其中最大的問題就是:包含引用類型的原型屬性會被全部實例共享。

而這也正是爲何要在構造函數中,而不是在原型對象定義屬性的緣由。在經過原型來實現繼承時,原型實際上會變成另外一個類型的實例。因而,原先的實例屬性也就變成了如今的原型屬性了。

function SuperType(){
    this.colors = ["red", "blue", "green"];
}

function SubType(){            
}
        
//inherit from SuperType
SubType.prototype = new SuperType();

var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors);    //"red,blue,green,black"
        
var instance2 = new SubType();
alert(instance2.colors);    //"red,blue,green,black"

SuperType定義了一個colors的實例對象屬性,該屬性包含一個數組(引用類型值),SuperType的每一個對實例都會有本身的colors屬性。當SubType經過原型鏈繼承了SuperType以後,SubType.prototype就成了SuperType的一個實例,所以它也擁有了一個它本身的colors屬性,就更專門建立了SubType.prototype.colors屬性同樣。也正由於如此,因此SubType構造函數的實例對象都會經過原型鏈繼承這個屬性,當其中某個實例對象改變了這個屬性,在其餘實例中也會體現出來。

在看看下面這種狀況

function SuperType(){
    this.colors = ["red", "blue", "green"];
}

function SubType(){            
}
        
//inherit from SuperType
SubType.prototype = new SuperType();

var instance1 = new SubType();
instance1.colors= ["blue", "green"];//重寫了colors屬性,如今這個屬性是屬於instance1本身的,也就是再也不是對原型對象上的屬性引用了
alert(instance1.colors);    //"blue,green"
        
var instance2 = new SubType();
alert(instance2.colors);    //"red,blue,green"//引用(繼承)原型對象上的屬性,實際屬性仍是部署在原型對象上的。

這個實例與前面的實例有什麼區別:colors都是原型屬性,因此每一個實例對象都會繼承這個屬性,若是沒有重寫這個屬性,那麼實例對象的這個屬性都是對原型對象的該屬性的引用,改變它會反映到其餘實例對象中,若是重寫了,那麼這個屬性就會覆蓋原來的屬性,從而變成自身的屬性。

4.肯定原型和實例的關係的方法:

(1)經過instanceof

alert(instance instanceof Object) //true

(2)經過isPrototypeof

alert(Object.prototype.isPrototypeof(instance)); //true

參考

(1)JavaScript高級程序設計第六章

(2)Javascript 面向對象編程

相關文章
相關標籤/搜索