本篇文章為大家展示了如何從Java的角度理解Ext的extend,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。

成都創(chuàng)新互聯(lián)公司專業(yè)為企業(yè)提供東遼網(wǎng)站建設(shè)、東遼做網(wǎng)站、東遼網(wǎng)站設(shè)計、東遼網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、東遼企業(yè)網(wǎng)站模板建站服務(wù),10多年東遼做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。
在Java中,我們在實現(xiàn)繼承的時候存在下面幾個事實:
1, 準(zhǔn)備兩個類,他們用extends關(guān)鍵字鏈接起來
2, 如果超類沒有默認(rèn)構(gòu)造函數(shù),需要在子類構(gòu)造函數(shù)中顯式的super并傳參,如果都是默認(rèn)構(gòu)造函數(shù)也可以super,不super虛擬機(jī)是自動的
3, 子類可追加,覆蓋,重載方法,子類可以有自己的私有屬性,他們在子類構(gòu)造函數(shù)中被構(gòu)造
4, 字段是數(shù)據(jù),方法在代碼區(qū),和類建立方法表,同一個類的對象有自己的數(shù)據(jù)但是共享方法代碼
比如有兩個類,Plane和Space,Plane表示平面,Space表示空間,Space是Plane的子類,在java中
/** * 根據(jù)字段數(shù)量分配內(nèi)存塊 * 實例化的時候虛擬機(jī)調(diào)用Plane.Plane方法把這個內(nèi)存塊作為this和構(gòu)造參數(shù)傳進(jìn)去,初始化完數(shù)據(jù)字段。 * 建立方法表映射 */ class Plane { protected int x; protected int y; Plane(int x, int y) { this.x = x; this.y = y; } public void XY() { System.out.println(x * y); } } /** * 自動擁有了超類的行為,但是超類的屬性需要超類去構(gòu)造 * 子類可構(gòu)造自己的屬性,添加自己的方法,覆蓋超類的方法 * <p/> * 按照繼承結(jié)構(gòu)的所有字段分配內(nèi)存塊,調(diào)用Space.Space將這個內(nèi)存塊作為this和參數(shù)一起傳進(jìn)去 * 把超類的那部分給超類,然后自己初始化自己的。 * <p/> * 建立方法表 */ class Space extends Plane { private int z; Space(int x, int y, int z) { super(x, y); this.z = z; } public void XYZ() { System.out.println(x * y * z); } } public class Test { public static void main(String[] args) { Plane plane = new Plane(2,3); plane.XY(); Space space = new Space(2, 3, 4); space.XYZ(); } }那么在js中也一樣,區(qū)別是代碼要放到構(gòu)造函數(shù)(可以理解為Java中的類)的原型上,原型是放置方法和不變屬性的理想場所,原型是一個對象,它和普通對象唯一不同的就是他有一個constructor屬性指向它所依附的構(gòu)造器,在java中子類查找屬性和方法是通過虛擬機(jī)來完成,但是在js中需要通過原型鏈來完成。也就是說繼承關(guān)系對程序員是不透明的,需要了解這個原型機(jī)制,原型機(jī)制上存在兩條鏈,一是原型鏈,二是構(gòu)造函數(shù)鏈。
仿照上面java的代碼,我們可以完成js的寫法,如下:
var Plane = function(x, y) { this.x = x; this.y = y; }; Plane.prototype.XY = function() { alert(this.x * this.y); }; var Space = function(x, y, z) { //用this調(diào)用超類構(gòu)造函數(shù),沒有java的super自動調(diào)用,所以要手動調(diào)用 Plane.call(this, x, y); //Space.superclass.constructor.call(this, x, y); 可以用一個統(tǒng)一的語法 //構(gòu)造自己的數(shù)據(jù) this.z = z; }; Space.prototype.XYZ = function() { alert(this.x * this.y * this.z); }JS中函數(shù)的this指函數(shù)的調(diào)用者,不管是java還是js,this都可理解為新分配的那段容納對象的內(nèi)存。在java 中通過Space extends Plane,虛擬機(jī)就維護(hù)好了他們的繼承關(guān)系以完成繼承關(guān)系的自動查找,但是在js中需要我們手動的處理,在這個時候Space是調(diào)用不到XY這個方法的,因為他們沒有在原型鏈上。我們可以開發(fā)一個函數(shù)來模擬java的關(guān)鍵字extends,比如這個函數(shù)叫做extend,通過執(zhí)行extend(Plane,Space)完成原型鏈的組裝。
那么extend怎么實現(xiàn)呢?首先要明白原型鏈,子類和父類在原型鏈上的關(guān)系是Space.prototype._proto_ == Plane.prototype,如果你理解不了,那就看String這個類吧,String.prototype._proto_ == Object.prototype,即String的原型會鏈接到Object的原型上,鏈接是通過_proto_這個屬性來完成的。_proto_是一個只讀的屬性,只能通過構(gòu)造函數(shù)寫入,所以String是Object的子類。
現(xiàn)在Plane的prototype._proto_ 等于Object,Space的prototype._proto_也等于Object,我們要在extend函數(shù)變換這個關(guān)系,即完成Space.prototype._proto_ == Plane.prototype,我們知道一個對象的_proto_要指向某個構(gòu)造函數(shù)的原型,需要讓這個對象由那個構(gòu)造函數(shù)構(gòu)造,那么我們只需要讓Space.prototype = new Plane()就可以了,這個時候Space.prototype._proto_ == Plane.prototype,而不再指向Object,原型還有一個屬性constructor指向原型所在的構(gòu)造器,由于Space.prototype剛被Plane創(chuàng)建出來,還沒有這個屬性,我們要手動賦值上去,代碼是Space.prototype. constructor = Space。這樣extend的責(zé)任就完成了。
但是這里有兩個問題:
1, 由于Space的原型在extend中被替換了,那么它原有的方法就沒有了。
2, Space的原型是Plane構(gòu)造的,雖然做到了Space.prototype._proto_ == Plane.prototype,但是Plane也在原型上寫入了x,y這兩個垃圾數(shù)據(jù),他們都是undefined,沒有意義,所以要手動刪除掉,這樣extend這個方法就不能通用了。
首先解決***個問題,我們要變化一點思路,利用js中函數(shù)也是數(shù)據(jù)的特性,我們把Space的那些方法拷貝到一個對象中,比如
var sbm= { XYZ : function() { alert(this.x * this.y * this.z); } };把這個sbm也傳遞給extend,extend在替換完原型后將sbm上的所有方法復(fù)制到Space的原型上即可,sbm是一個對象直接量,用json語法。現(xiàn)在的extend就變?yōu)榱巳齻€參數(shù),即extend(sb,sp,sbm),sb是子類,sp是超類,sbm是子類要放到原型上的方法。
對于第二個問題,本質(zhì)原因是Plane這個函數(shù)要完成一些數(shù)據(jù)初始化,它是超類,我們不能控制,我們只關(guān)心Plane的原型而不關(guān)心它構(gòu)造什么數(shù)據(jù),所以我們可以把它的原型單獨(dú)拿出來,再定義一個干凈的函數(shù),這個函數(shù)不做任何事,將這個干凈函數(shù)的原型設(shè)置為Plane的原型,再用這個干凈函數(shù)構(gòu)造一個對象,這樣出來的對象就是是干凈的,也完成了_proto_指向了Plane.prototype,***!有了這兩個方法,我們就可以開始實現(xiàn)這個extend,代碼如下:
var extend = function(sb, sp, sbm) { var F = function() { },sbp,spp = sp.prototype; F.prototype = spp; //用干凈函數(shù)嫁接得到子類原型 sbp = sb.prototype = new F(); sbp.constructor = sb; //然后指定一個constructor指回子類 //把sbm的上的屬性拷貝到子類的原型上 for (var p in sbm) { sbp[p] = sbm[p]; } };那么完成Space繼承Plane的代碼如下:
extend(Space, Plane, { XYZ : function() { alert(this.x * this.y * this.z); } }); var spaceObject = new Space(2, 3, 4); spaceObject.XY();//成功調(diào)用超類方法 spaceObject.XYZ();OK,到了這里,我們基本上就完成任務(wù)了,完全從java的方向搞定的。我們現(xiàn)在利用js的特性來優(yōu)化,讓使用extend更加簡單。
我們說在java中必須寫兩個類,每個類都寫自己的字段 ,構(gòu)造函數(shù),方法等,在我們實現(xiàn)的extend函數(shù)中也確實把子類,父類都傳遞了進(jìn)來,但是我們多了一個參數(shù),那就是子類的方法集合即sbm,***個參數(shù)sb本身也是函數(shù),那是不是可以將這個函數(shù)也放進(jìn)sbm傳進(jìn)來呢?這樣extend就變?yōu)閮蓚€參數(shù),即extend(sp,sbm),現(xiàn)在extend返回一個函數(shù),返回的這個函數(shù)就是sp的子類,這是完全可行的,我們叫做extend2吧。
var extend2 = function(sp, sbm) { var sb = sbm.constructor; //如果說沒有顯式的構(gòu)造函數(shù),那么子類就是直接調(diào)用超類構(gòu)造函數(shù) if (sb == Object) { sb = function() { sp.apply(this, arguments); }; } extend(sb, sp, sbm); return sb; }我們說要把子類的構(gòu)造函數(shù)放到sbm上,放上去的key叫做constructor,就表示構(gòu)造器,js中每一個對象都一個constructor屬性,它指向構(gòu)造了這個對象構(gòu)造函數(shù)。sbm本來是個Object對象,它的constructor就指向Object,這個constructor是在sbm關(guān)聯(lián)的那個原型上的,現(xiàn)在我們在sbm上設(shè)置某個子類的構(gòu)造函數(shù),這個時候表示sbm有個自己的constructor。
現(xiàn)在我們在extend2中要做的事情就是提取出構(gòu)造函數(shù),然后還原為三個參數(shù)去調(diào)用之前的extend,在java中我們的子類是可以不用構(gòu)造器的,只要父類也有默認(rèn)的構(gòu)造器,那么在這里一樣,sbm可能不包含constructor,那么我們需要做一個函數(shù),它調(diào)用父類的構(gòu)造函數(shù),在java中這種情況過程是自動的。所以當(dāng)sbm.constructor為Object的時候表示sbm沒有指定構(gòu)造函數(shù),這個時候?qū)?/p>
sb = function() { sp.apply(this, arguments); };即調(diào)用父類構(gòu)造函數(shù)。這樣將sb,sp,sbm傳遞給extend就可以了。
這個時候我們新的繼承語法如下:
var NewSpace = extend2(Plane, { constructor : function(x, y, z) { Plane.call(this, x, y); this.z = z; }, XYZ : function() { alert(this.x * this.y * this.z); } }); var newObject = new NewSpace(3, 4, 5); newObject.XY(); newObject.XYZ();和prototype.js和mootolls的實現(xiàn)比較,大同小異
// properties are directly passed to `create` method var Person = Class.create({ initialize: function(name) { this.name = name; }, say: function(message) { return this.name + ': ' + message; } });var Animal = new Class({ initialize: function(age) { this.age = age; }, say : function() { alert(this.age); } }); var Cat = new Class({ Extends: Animal, initialize: function(name, age) { this.parent(age); // calls initalize method of Animal class this.name = name; } });到了這里其實已經(jīng)差不多了,但是細(xì)心的讀者會發(fā)現(xiàn),我們在extend中會把sbm的所有屬性拷貝到子類的原型上,這里豈不是就要把constructor也拷貝到原型上?如果sbm包含了這個constructor其實就無所謂,因為子類的原型的constructor本來就是需要指向這個構(gòu)造函數(shù)的,但是sbm上沒有constructor那豈不是要把Object拷貝到子類原型上,答案是不會的,我們在拷貝的時候用的for in循環(huán)是迭代不出默認(rèn)的那個constructor的。
現(xiàn)在我們來看看Ext.extend,應(yīng)該完全沒有問題了。我們用了兩個方法extend,extend2,Ext把它合并為了一個方法Ext.extend,所以它會判斷傳進(jìn)來的參數(shù)然后進(jìn)行變換,這樣Ext.extend就支持兩個參數(shù)和三個參數(shù)進(jìn)行調(diào)用。對于前面用到拷貝屬性,Ext做了一個工具函數(shù)叫做Ext.apply,對于將一個對象的屬性拷貝到一個類的原型上,Ext做了一個工具類叫做Ext.override。
Ext.extend = function() { // inline overrides 把傳入的對象屬性復(fù)制到到this中 var io = function(o) { for (var m in o) { this[m] = o[m]; } }; //oc其實就是Object函數(shù) var oc = Object.prototype.constructor; return function(sb, sp, overrides) { //如果第二個參數(shù)是個對象而不是類,那么是用兩個參數(shù)調(diào)用的,***個參數(shù)是父類,第二個參數(shù)是對象 if (typeof sp == 'object') { overrides = sp; //將第三個參數(shù)換為對象 sp = sb; //把***個參數(shù)賦值第二個當(dāng)成父類 sb = overrides.constructor != oc ? overrides.constructor : function() { sp.apply(this, arguments); }; //子類這個構(gòu)造函數(shù)要么是外界傳入的名字為constructor,要么就是直接調(diào)用超類構(gòu)造函數(shù)的一個函數(shù) //傳入的constructor除了構(gòu)造自己還要調(diào)用超類的構(gòu)造函數(shù) } /** * 繼承的兩種參數(shù) * 1,自己寫一個構(gòu)造函數(shù),初始化一些字段,然后調(diào)用超類構(gòu)造函數(shù),再寫一個json對象,里面是要覆蓋超類的方法或者追加的方法 * 然后這樣調(diào)用extend(sub,sup,{over1:f,over2:f,addf:f}),就像java的語法 * SubClass extend SuperClass { * SubClass(){ * super(); * } * } * * 2,***種可以理解為模擬java,但是因為構(gòu)造函數(shù)也是數(shù)據(jù),所以完全可以把構(gòu)造函數(shù)也放進(jìn)那個jdon對象,只不過約定好一個名字 * 比如constructor,然后這樣調(diào)用 * extend(sup,{constructor:f,over1:f,over2:f,addf:f}) */ var F = function() { }, sbp, spp = sp.prototype; F.prototype = spp; sbp = sb.prototype = new F(); //以上用干凈函數(shù)嫁接得到子類原型 sbp.constructor = sb; //然后指定一個constructor指回子類,這樣就大工告成 sb.superclass = spp; //在子類上指定一個靜態(tài)字段指向超類原型,這樣在子類構(gòu)造函數(shù)中可訪問超類構(gòu)造函數(shù)sub.superclass.constructor.call(this, config) /** * 這段代碼是防御性的,在自己實現(xiàn)繼承的時候,可能會出現(xiàn)原型上的構(gòu)造函數(shù)指向問題,所以如果發(fā)現(xiàn)某個超類 * 的構(gòu)造函數(shù)是object,要么這個超類卻是Object,要么出現(xiàn)了失誤,所以這里再一次重設(shè)置一下,以防萬一,這個代碼我們在分析Ext的Observable的時候會提到的它的作用 */ if (spp.constructor == oc) { spp.constructor = sp; } //子類上方一個靜態(tài)的重寫方法,注意js沒有重載,可以用來重寫子類原型上的函數(shù) sb.override = function(o) { Ext.override(sb, o); }; //用一個閉包在子類原型上引用一個超類原型,引用的是一個函數(shù) sbp.superclass = sbp.supr = (function() { return spp; }); //子類原型上放置一個重寫函數(shù),可以用來覆蓋具體實例對象 sbp.override = io; //在子類原型上重寫或添加函數(shù) Ext.override(sb, overrides); //子類上直接放一個靜態(tài)繼承方法,貌似實現(xiàn)多繼承 sb.extend = function(o) { return Ext.extend(sb, o); }; return sb; }; }();現(xiàn)在使用Ext的extend來實現(xiàn)我們之前的繼承代碼就如下
var Plane = function(o) { this.x = o.x; this.y = o.y; }; Plane.prototype.XY = function() { alert(this.x * this.y); }; var Space = Ext.extend(Plane, { constructor : function(o) { Space.superclass.constructor.call(this, o); this.z = o.z; }, XYZ : function() { alert(this.x * this.y * this.z); } }); var space = new Space({ x:2,y:3,z:4}); space.XY(); space.XYZ();現(xiàn)在我們來分析一下Ext中的繼承重頭戲Observable,它位于Ext.util這個包下,它的意思即是觀察者,使用觀察者模式,EDA模式,UI組件就利用這種基于觀察和事件的機(jī)制進(jìn)行通信和渲染。
所有的UI組件都繼承這個類,我們看看它的構(gòu)造函數(shù)
EXTUTIL.Observable = function(){ var me = this, e = me.events; if(me.listeners){ me.on(me.listeners); delete me.listeners; } me.events = e || {}; };這個構(gòu)造函數(shù)不需要參數(shù),在java中,這種父類的構(gòu)造可以自動的調(diào)用默認(rèn)構(gòu)造函數(shù),但是這里要注意,if(me.listeners)依賴了子類的構(gòu)造行為,這在面向?qū)ο笤瓌t中似乎是一個禁忌,但是如果一個繼承體現(xiàn)完全由一個團(tuán)隊維護(hù),他們同時制定繼承規(guī)則和繼承規(guī)范,這也無可厚非,這里的listeners可以在子類中不提供,可以讓構(gòu)造出來的對象自己調(diào)用on方法來添加監(jiān)聽器,同理這里的events,如果子類沒構(gòu)造會被賦值為一個空對象。那么這個Observable構(gòu)造器做了兩個事,一個是看子類是否在對象上放了監(jiān)聽器,如果放了,就調(diào)用對象的on方法進(jìn)行事件和監(jiān)聽的綁定,二是看子類是否在對象上放置了events,如果沒有就把對象的events屬性設(shè)置為一個空對象。也就是說子類是完全可以不做任何事的,子類只負(fù)責(zé)自己的數(shù)據(jù)構(gòu)造和行為覆蓋或追加,events在和監(jiān)聽器綁定之后就是一個Ext.util.Event對象的容器,見這行代碼: me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);也就是說,在Ext中,一個活生生的能夠響應(yīng)事件的對象有一個Event容器,它保存了這個對象可以響應(yīng)什么事件以及事件被觸發(fā)后被調(diào)用的監(jiān)聽器。
Observable原型上放置的方法都是子類繼承的方法,子類的對象就可以在運(yùn)行時調(diào)用這些方法,如下:
原型上放置了一個靜態(tài)變量和一些方法,這些方法都是和事件以及監(jiān)聽有關(guān),注意Observable的原型是一個新的對象直接量,它的constructor屬性肯定指向的是Object,不是指向的Observable,這豈不是存在bug,我通過代碼檢測發(fā)現(xiàn)alert(Ext.util.Observable.prototype.constructor == Ext.util.Observable);的結(jié)果確實又是true,怎么回事呢?答案就在Ext.extend的那段防御性代碼,大家回過去看看吧!
現(xiàn)在我們寫一個繼承Obervable的類,不過不是UI組件,而是一個領(lǐng)域模型,比如論壇帖子,它在被修改之后會跑出一個被修改的事件,監(jiān)聽器捕獲這個事件將修改保存到數(shù)據(jù)庫中,代碼如下:
ForumThread = Ext.extend(Ext.util.Observable, { constructor: function(config) { this.name = config.name; //把監(jiān)聽器放進(jìn)超類的屬性 this.listeners = config.listeners; this.events = {"change" : true}; //給領(lǐng)域模型設(shè)置事件,通過上面的寫法也可以 /* this.addEvents({ "change" : true });*/ //調(diào)用超類構(gòu)造超類不變量 ForumThread.superclass.constructor.call(this, config) }, //領(lǐng)域行為,會觸發(fā)事件 changeName : function(newName) { alert("原主題名字是:" + this.name); this.name = newName; alert("更改后主題名字是:" + this.name); this.fireEvent("change", this);//觸發(fā)事件 } }); Ext.onReady(function() { var forumThread = new ForumThread({ name : '關(guān)于將Jdon框架提升為DCI框架的設(shè)想', //構(gòu)造領(lǐng)域模型時注入監(jiān)聽處理程序 listeners : { change : function(thread) { alert('接受到事件,將異步保存新的名字:' + thread.name); } } }); //領(lǐng)域行為調(diào)用 forumThread.changeName("關(guān)于將Jdon框架提升為DCI框架的設(shè)想,整合JdonMVC"); });如果事件設(shè)置和監(jiān)聽綁定直接在子類完成,那么就不必顯式調(diào)超類構(gòu)造函數(shù)
ForumThread = Ext.extend(Ext.util.Observable, { constructor: function(config) { this.name = config.name; this.events = {"change" : true}; this.on(config.listeners); }, //領(lǐng)域行為,會觸發(fā)事件 changeName : function(newName) { alert("原主題名字是:" + this.name); this.name = newName; alert("更改后主題名字是:" + this.name); this.fireEvent("change", this);//觸發(fā)事件 } });上述內(nèi)容就是如何從Java的角度理解Ext的extend,你們學(xué)到知識或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識儲備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
當(dāng)前標(biāo)題:如何從Java的角度理解Ext的extend
文章網(wǎng)址:http://www.chinadenli.net/article0/gpdeoo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供電子商務(wù)、品牌網(wǎng)站制作、做網(wǎng)站、動態(tài)網(wǎng)站、網(wǎng)站制作、品牌網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)