javascript 原型鏈維護和繼承詳解
一.兩個原型
很多人都知道javascript是原型繼承,每個構造函數都有一個prototype成員,通過它就可以把javascript的繼承演義的美輪美奐了.
其實啊,光靠這一個屬性是無法完成javascript的繼承.
我們在代碼中使用的prototype完成繼承在這里就不多說了.大家可以查一下資料.
另外一個看不見的prototype成員.
每一個實例都有有一條指向原型的prototype屬性,這個屬性是無法被訪問到的,當然也就無法被修改了,因為這是維護javascript繼承的基礎.
//構造器聲明
function Guoyansi(){ }
function GuoyansiEx(){}
//原型繼承
GuoyansiEx.prototype=new Guoyansi();
//創(chuàng)建對象
var g1=new GuoyansiEx();
var g2=new GuoyansiEx();
上面的代碼中的對象可以用下面的圖來說明
二.原型的維護
一個構造器產生的實例,其constructor屬性總是指向該構造器.我們暫且認為該話是對的.
function Guoyansi(){ }
var obj1=new Guoyansi();
console.log(obj1.constructor===Guoyansi);//true
其實構造器本身是沒有constructor這個屬性的,那么這個屬性是來自哪呢?
答案是:來自原型.
因此得出下面的結論
既然我們可以通過constructor來尋找構造器.因此我們就可以進一步完善上面的圖了.
function GuoyansiEx(){}
GuoyansiEx.prototype=new Guoyansi();
console.log(GuoyansiEx.constructor===GuoyansiEx)//false
根據上圖,上面的結果應該是true,但為什么是false呢?
現在做個分析.
GuoyansiEx的原型被Guoyansi的實例重寫了,那么GuoyansiEx的原型中的constructor自然也是來自Guoyansi的實例.
而Guoyansi實例中的constructor又是來自Guoyansi.prototype.而Guoyansi.prototype沒有被重寫,
所以Guoyansi.prototype的constructor指向Guoyansi(構造函數);
根據以上分析得出下面的結論
如果在開發(fā)過程中對于Constructor的指向要求非常精確的話,可以做如下處理.
/**方法一:**/
function Guoyansi(){}
function GuoyansiEx(){}
GuoyansiEx.prototype=new Guoyansi();
GuoyansiEx.prototype.constructor=GuoyansiEx;//重置constructor指向.
/**
方法二
**/
function Guoyansi(){}
function GuoyansiEx(){
this.constructor=arguments.callee;
}
GuoyansiEx.prototype=new Guoyansi();
/**
方法三
**/
function Guoyansi(){}
function GuoyansiEx(){
this.constructor=GuoyansiEx;
}
GuoyansiEx.prototype=new Guoyansi();
三.看不見的原型有什么用呢?
看得見的原型鏈我們可以對他操作來完成我們的繼承,那么這個看不見的原型鏈我們既看不見,又無法操作.要它有何用.
面向對象中繼承有一個特性:相似性.子類與父類具有相似性.因此在子類中你是無法用delete刪除從父類繼承而來的成員.也就是說子類必須具有父類的特性.
為了維護這個特性,javascript在對象的內部產生了一條我們看不見的原型屬性,并且不允許用戶訪問.這樣,用戶可以處于任何目的來修改constructor,
而不會破壞子類擁有父類的特性.
簡而言之:內部原型是javascript的原型繼承機制所需要的,而外部原型是用戶實現繼承所需要的.
四.火狐引擎SpiderMonkey中的__proto__
還是這段代碼.
function Guoyansi(){}
Guoyansi.prototype.age=24;
function GuoyansiEx(){}
var obj1=new Guoyansi();
GuoyansiEx.prototype=obj1;
GuoyansiEx.prototype.constructor=GuoyansiEx;//重置constructor指向.
var obj2=new GuoyansiEx();
我現在想要從obj開始向上訪問父類Guoyansi的prototype的屬性的age.
思路是這樣的.
第一步:obj2====>obj2.constructor.prototype
第二部:obj2.constructor.prototype===>GuoyansiEx.prototype;
第三部:GuoyansiEx.prototype===>obj1;
第四部:obj1.constructor====>Guoyansi
第五部:Guoyansi.prototype.age
寫成這這樣:console.log(obj2.constructor.prototype.constructor.prototype.age)//24;
最終的結果是24.
最終的結果是24.可以正常執(zhí)行,但是在好多書上說constructor修改后,級無法在找到父類中的原型了.不知道是怎么回事.
在火狐中提夠了一種更加簡潔的屬性._proto_
SpiderMonkey中默認在任何創(chuàng)建的對象上添加了一個名為_proto_的屬性,該屬性指向構造器所用的原型.
其實就是我們上面提到的不可見的原型鏈,只不過是在這個地方變相的公開而已.
可以這樣訪問到age
console.log(obj2.__proto__.__proto__.age);//24
這樣的確是成功的訪問到了父類的原型屬性,但是這個屬性只適用于火狐,在其他瀏覽器中是會出錯的.
在E5中對Object做出了擴展Object.getPrototypeOf(),可以訪問到所有父類的原型了.
function Guoyansi(){}
Guoyansi.prototype.age=24;
function GuoyansiEx(){}
var obj1=new Guoyansi();
GuoyansiEx.prototype=obj1;
GuoyansiEx.prototype.constructor=GuoyansiEx;//重置constructor指向.
var obj2=new GuoyansiEx();
var proto=Object.getPrototypeOf(obj2);
while(proto){
console.log(proto.constructor);
proto=Object.getPrototypeOf(proto);
}
console.log("object的原型"+proto);
結果是:GuoyansiEx
Guoyansi
Object
object的原型null
個人覺得這些應該算是javascript面向對象的精髓之一了.小伙伴們自己參考下,根據需求使用到自己的項目中去吧
相關文章
Javascript json object 與string 相互轉換的簡單實現
下面小編就為大家?guī)硪黄狫avascript json object 與string 相互轉換的簡單實現。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-09-09Bootstrap Table從服務器加載數據進行顯示的實現方法
Bootstrap-Table是一個Boostrap的表格插件,能夠將JSON數據直接顯示在表格中。接下來通過本文給大家分享Bootstrap Table從服務器加載數據進行顯示的實現方法,感興趣的朋友一起看看吧2016-09-09firefox 和 ie 事件處理的細節(jié),研究,再研究 書寫同時兼容ie和ff的事件處理代碼
firefox 和 ie 事件處理的細節(jié),研究,再研究 書寫同時兼容ie和ff的事件處理代碼2007-04-04JavaScript格式化日期時間的方法和自定義格式化函數示例
JavaScript默認的時間格式我們一般情況下不會用,所以需要進行格式化,下面說說我總結的JavaScript時間格式化方法2014-04-04Handtrack.js庫實現實時監(jiān)測手部運動(推薦)
這篇文章主要介紹了實時監(jiān)測手部運動的 JS 庫,可以實現很多有趣功能,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02