<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    捕風(fēng)之巢

    統(tǒng)計

    留言簿(3)

    java友情鏈接

    閱讀排行榜

    評論排行榜

    javascript中子類調(diào)用父類方法解決方案

    幾乎每位在開發(fā)JavaScript時嘗試應(yīng)用面向?qū)ο蠹夹g(shù)的開發(fā)者,或多或少都會問自己一個問題:“如何調(diào)用父類(super class)的方法?”在Ajax技術(shù)還沒有目前這樣炙手可熱之前,這種問題很少出現(xiàn),因為大多數(shù)開發(fā)者僅在進行客戶端form驗證或者簡單的 DHTML/DOM操作時使用JavaScript。在那些簡單的解決方案中,函數(shù)式編程(functional programming)是很有意義的,面向?qū)ο缶幊虅t處在次之重要的位置。現(xiàn)在,Ajax技術(shù)發(fā)展勢頭迅猛,開發(fā)者已經(jīng)建立了一個調(diào)用大量客戶端JavaScript、不斷增長的、復(fù)雜的系統(tǒng)。因此,在JavaScript上嘗試OO技術(shù)便成為了管理復(fù)雜性的一種手段。在此過程中,多數(shù)開發(fā)者很快便認識到:JavaScript 是一種原型化的(prototypical)語言,它缺少OO自身帶來的多種便利。

    ????????OO設(shè)計的主旨和關(guān)于它的一些話題談起來很大,但只著眼于Class的定義方式,我認為它是JavaScript開發(fā)者嘗試解決問題的首選。因此,你可以在互聯(lián)網(wǎng)上找到許多不同的問題解決案例,但在我看過它們后不免有些失望——這些案例都是在某個場合下適用,而不是放之四海而皆準的通法。而我對這個話題的興趣來自于我的team在開發(fā)ThinWire Ajax Framework的影響。由于這個框架生成出對客戶端代碼的需求,才使我們“被迫”去實現(xiàn)可靠的、支持父類方法調(diào)用的OO模式。通過父類調(diào)用,你可以進一步依靠類的繼承特性來核心化通用代碼,從而更易于減少重復(fù)代碼,去掉客戶端代碼的壞味道

    ????????下面羅列出了一些在我的研究過程中遇到的解決方式。最終,我沒有從中找出一個可以接收的解決方案,于是我不得不實現(xiàn)一個自己的解決方案,你將在本文的結(jié)尾部分看到這個方案。

    ????????然而父類調(diào)用在這里是最重要的OO機制,因此我需要一個相應(yīng)的工作模式,也正是因為在我的觀點中原型化方式是丑陋的,所以我更需要一種更加自然地使用JavaScript定義類的方法。

    More Solutions:

    ????????好吧,讓我們進入討論。正如開發(fā)者所察覺的那樣,在JS中實現(xiàn)基本的繼承是很容易的事,事實上有一些眾所周知的方法:

    丑陋的Solution:

    ????????沒有進行父類調(diào)用的簡單繼承:

    // 提前寫好的JavaScript Class定義和繼承
    // 當然,這種代碼很丑陋,散發(fā)著代碼的壞味道。
    function BaseClass() {
    ????//BaseClass constructor code goes here
    }

    BaseClass.prototype.getName = function() {
    ????return "BaseClass";
    }

    function SubClass() {
    ????//SubClass constructor code goes here
    }

    //Inherit the methods of BaseClass
    SubClass.prototype = new BaseClass();

    //Override the parent's getName method
    SubClass.prototype.getName = function() {
    ????return "SubClass";
    }

    //Alerts "SubClass"
    alert(new SubClass().getName());


    導(dǎo)致IE內(nèi)存泄露的Solution:

    ????????這種實現(xiàn)方式能夠?qū)е略贗E中的內(nèi)存泄漏,你應(yīng)該盡量避免:

    // 運行時的JavaScript Class 定義和繼承
    // 看上去很傳統(tǒng),但這些腳本會導(dǎo)致在Internet Explorer中的內(nèi)存泄漏.
    function BaseClass() {
    ????this.getName = function() {
    ????????return "BaseClass";
    ????};????

    ????//BaseClass constructor code goes here
    }

    function SubClass() {
    ????//在對象實例建立時重載父類的getName方法
    ????this.getName = function() {
    ????????return "SubClass";
    ????}

    ????//SubClass constructor code goes here
    }

    //Inherit the methods of BaseClass
    SubClass.prototype = new BaseClass();

    //Alerts "SubClass"
    alert(new SubClass().getName());
    ????????

    ????????就像我在第一個實現(xiàn)方法中所注釋的那樣,第一個實現(xiàn)方法有些丑陋,但它相比引起內(nèi)存泄漏的第二種方式便是首選了。

    ????????我把這兩種方法放在這里的目的是指出你不應(yīng)該使用它們。

    硬性編碼的Solution:

    ????????讓我們看一下第一個例子,它采用了標準的原型化方式,但問題是:它的子類方法如何調(diào)用父類(基類)方法?下面是一些開發(fā)者嘗試并采用的方式:

    ????????一種企圖進行父類調(diào)用的“通病”:

    function BaseClass() { }
    BaseClass.prototype.getName = function() {
    ????return "BaseClass(" + this.getId() + ")";
    }

    BaseClass.prototype.getId = function() {
    ????return 1;
    }

    function SubClass() {}
    SubClass.prototype = new BaseClass();
    SubClass.prototype.getName = function() {
    ????//調(diào)用父類的getName()方法
    ????//哈哈,這是對父類調(diào)用的直接引用嗎?
    ????return "SubClass(" + this.getId() + ") extends " +
    ????????BaseClass.prototype.getName();
    }

    SubClass.prototype.getId = function() {
    ????return 2;
    }

    //輸出結(jié)果:"SubClass(2) extends BaseClass(1)";
    //這是正確的輸出嗎?
    alert(new SubClass().getName());


    ???????? 上面的代碼是對第一段腳步進行修改后的版本,我去掉了一些注釋和空格,使你能注意到新的getId()方法和對父類的調(diào)用。你一定急于知道通過這樣對 BaseClass的硬性編碼引用(hard coded reference),它是否能進行正確地調(diào)用BaseClass的方法?

    ???????? 一個正確的、多態(tài)的父類調(diào)用必做的事情是保證“this”引用指向當前對象實例和類方法。在這里,看上去和它應(yīng)該輸出的結(jié)果非常接近,看上去好像在 SubClass中調(diào)用了BaseClass的getName()方法。你發(fā)現(xiàn)問題了嗎?這個問題是非常細小的,但卻很重要決不能忽視。通過使用上面的父類調(diào)用語法,BaseClass的getName()方法被調(diào)用,它返回一個字符串:包括類名和“this.getId()”的返回值。問題在于 “this.getId()”應(yīng)該返回2,而不是1。如果這和你所想的不同,你可以查看Java或者C#這類OO語言的多態(tài)性。

    改進后的硬性編碼Solution:

    ????????你可以通過一個微小的改動來解決這個問題。

    靜態(tài)(硬編碼)父類調(diào)用:

    function BaseClass() { }
    BaseClass.prototype.getName = function() {
    ????return "BaseClass(" + this.getId() + ")";
    }

    BaseClass.prototype.getId = function() {
    ????return 1;
    }

    function SubClass() {}
    SubClass.prototype = new BaseClass();
    SubClass.prototype.getName = function() {
    ????//一點魔法加上多態(tài)性!
    ????//但很明顯,這還是一個直接引用!????
    ????return "SubClass(" + this.getId() + ") extends " +
    ????????BaseClass.prototype.getName.call(this);
    }

    SubClass.prototype.getId = function() {
    ????return 2;
    }

    //輸出結(jié)果:"SubClass(2) extends BaseClass(2)";
    //Hey, 我們得到了正確的輸出!
    alert(new SubClass().getName());


    ????????在ECMA-262 JavaScript/EcmaScript標準中, Call()方法是所有Function實例的一個成員方法,這已經(jīng)被所有的主流瀏覽器所支持。JavaScript把所有的function看作對象,因此每個function都具有方法和附著其上的屬性。Call()方法允許你調(diào)用某個function,并在function的調(diào)用過程中確定 “this”變量應(yīng)該是什么。JavaScript的function沒有被緊緊地綁定到它所在的對象上,所以如果你沒有顯式地使用call()方法, “this”變量將成為function所在的對象。

    ????????另外一種方法是使用apply方法,它和call()方法類似,只在參數(shù)上存在不同:apply()方法接受參數(shù)的數(shù)組,而call()方法接受單個參數(shù)。

    Douglas Crockford的Solution:

    ????????現(xiàn)在回溯到上面的示例,在這個示例中唯一的問題就是父類引用是直接的、硬性編寫的。它可以適用于小型的類繼承環(huán)境,但對于具有較深層次的大型繼承來講,這些直接引用非常難于維護。

    ????????那么,有解決方法嗎?不幸的是這里沒有簡單的解決方案。

    ????????JavaScript沒有提供對通過“隱性引用”方式調(diào)用父類方法的支持,這里也沒有在其它OO語言中使用的“super”變量的等價物。于是,一些開發(fā)者做出了自己的解決方案,但就像我前面提到的那樣,每個解決方案都存在某種缺點。

    ????????例如,下面列出的眾多著名方法之一:JavaScript大師[ur=http://en.wikipedia.org/wiki/Douglas_Crockford]Douglas Crockford[/url]在他的《Classical Inheritance in JavaScript》中提出的方法。

    ????????Douglas Crockford的方法在多數(shù)情況下可以正常工作:

    一次性支持代碼:

    //Crockford的方法:給所有的function都增加'inherits' 方法、
    //每個類都增加了'uber'方法來調(diào)用父類方法
    Function.prototype.inherits = function(parent) {
    ????var d = 0, p = (this.prototype = new parent());
    ????
    ????this.prototype.uber = function(name) {
    ????????var f, r, t = d, v = parent.prototype;
    ????????if (t) {
    ????????????while (t) {
    ????????????????v = v.constructor.prototype;
    ????????????????t -= 1;
    ????????????}
    ????????????f = v[name];
    ????????} else {
    ????????????f = p[name];
    ????????????if (f == this[name]) {
    ????????????????f = v[name];
    ????????????}
    ????????}
    ????????d += 1;
    ????????r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));
    ????????d -= 1;
    ????????return r;
    ????};
    };


    運行示例:

    function BaseClass() { }
    BaseClass.prototype.getName = function() {
    ????return "BaseClass(" + this.getId() + ")";
    }

    BaseClass.prototype.getId = function() {
    ????return 1;
    }

    function SubClass() {}
    SubClass.inherits(BaseClass);
    SubClass.prototype.getName = function() {
    ????//這里看上去非常的清晰,它調(diào)用了BaseClass的getName()方法
    return "SubClass(" + this.getId() + ") extends " +
    ????????this.uber("getName");
    }

    SubClass.prototype.getId = function() {
    ????return 2;
    }

    function TopClass() {}
    TopClass.inherits(SubClass);
    TopClass.prototype.getName = function() {
    ????//這里看上去非常的清晰,它調(diào)用了SubClass的getName()方法
    ????return "TopClass(" + this.getId() + ") extends " +
    ????????this.uber("getName");
    }

    TopClass.prototype.getId = function() {
    ????//Ok, 因此this.getId()應(yīng)該總是
    //返回調(diào)用SubClass的getId()方法的返回值(2)。
    ????return this.uber("getId");
    }

    //輸出結(jié)果:"TopClass(2) extends SubClass(1) extends BaseClass(1)"
    //嗯?后面的兩次this.getId()調(diào)用都沒有返回2.
    //發(fā)生了什么?
    alert(new TopClass().getName());
    ????????
    ???????? 上面代碼的第一部分包括了Crockford的“inherit”和“uber”方法代碼。第二部分看上去和前面的示例很類似,除了我添加了用來演示 Crockford方式所存在問題的第三層繼承關(guān)系。誠然,Crockford這位JavaScript大師的方法是我所找到的最可靠的方法之一,我很敬佩他在JavaScript編程方面做出的貢獻。但是,如果你使用三個依次繼承的類來考核他的代碼,你將從輸出中發(fā)現(xiàn)這里存在著細微的問題。

    ???????? 從輸出結(jié)果看,第一次調(diào)用的this.getId()返回了TopClass當前的id值“2”,但在調(diào)用SubClass和BaseClass的 getName()方法時返回了“1”而不是“2”。從代碼上看,在getName()方法中的父類調(diào)用行為是正確的,三個類的名字都被正確地顯示出來。唯一的問題出現(xiàn)在this.uber("getId")這個父類調(diào)用被放入調(diào)用堆棧(call stack)時。因為此時當前對象是一個TopClass實例,而每次調(diào)用在調(diào)用堆棧中的this.getId()都應(yīng)該返回調(diào)用TopClass的 getId()方法后的返回值。

    ????????而問題是TopClass的this.getId()方法通過this.uber ("getId")執(zhí)行了父類調(diào)用,這三次this.getId()調(diào)用中的后兩次錯誤地調(diào)用了BaseClass的getId()方法,這樣便在輸出結(jié)果中顯示了兩次“1”。正確的行為應(yīng)該是調(diào)用三次SubClass的getId()方法,在輸出結(jié)果中顯示三次“2”。大家可以通過FireFox的FireBug插件進行代碼debug進行觀察。

    ????????這是十分難以描述的現(xiàn)象,我不能保證我能把它解釋清楚。但是至少從上面的運行結(jié)果中可以看出它是錯誤的。

    ????????另外,Crockford的方法和其它一些方法的劣勢在于每個父類調(diào)用都需要一個額外的方法調(diào)用和額外的某種處理。這是否成為你所面臨的問題,取決于你所使用的父類調(diào)用深度。在ThinWire項目的客戶端代碼中使用了大量的父類調(diào)用,因此父類調(diào)用的可靠性和快速性在項目中是很重要的。

    我的初級Solution:

    ????????面對這樣的窘境——Crockford的方法出現(xiàn)問題、在互聯(lián)網(wǎng)上沒有找到符合要求的方法,我決定看看我自己是否可以發(fā)明一種可以滿足要求的方法。這花掉了我近一周的時間來使代碼工作并滿足各種情況,但我對它的工作情況很有信心,并且很快把它與framework集成在一起,TinWire的beta和beta2兩個版本中都使用了這些“初級設(shè)計”的代碼。

    ????????動態(tài)父類調(diào)用:

    一次性支持代碼:

    //定義最頂級類
    function Class() { }
    Class.prototype.construct = function() {};
    Class.__asMethod__ = function(func, superClass) {????
    ????return function() {
    ????????var currentSuperClass = this.$;
    ????????this.$ = superClass;
    ????????var ret = func.apply(this, arguments);????????
    ????????this.$ = currentSuperClass;
    ????????return ret;
    ????};
    };

    Class.extend = function(def) {
    ????var classDef = function() {
    ????????if (arguments[0] !== Class) { this.construct.apply(this, arguments); }
    ????};
    ????
    ????var proto = new this(Class);
    ????var superClass = this.prototype;
    ????
    ????for (var n in def) {
    ????????var item = def[n];????????????????????????
    ????????
    ????????if (item instanceof Function) {
    ????????????item = Class.__asMethod__(item, superClass);
    ????????}
    ????????
    ????????proto[n] = item;
    ????}

    ????proto.$ = superClass;
    ????classDef.prototype = proto;
    ????
    ????//賦給這個新的子類同樣的靜態(tài)extend方法
    ????classDef.extend = this.extend;????????
    ????return classDef;
    };


    運行示例:

    //Hey, 注意一下這個類的定義方式
    //看上去比其它方式要清楚些
    var BaseClass = Class.extend({
    ????construct: function() { /* optional constructor method */ },
    ????
    ????getName: function() {
    ????????return "BaseClass(" + this.getId() + ")";
    ????},
    ????
    ????getId: function() {
    ????????return 1;
    ????}
    });

    var SubClass = BaseClass.extend({
    ????getName: function() {
    ????????//調(diào)用BaseClass的getName()方法
    ????????return "SubClass(" + this.getId() + ") extends " +
    ????????????this.$.getName.call(this);
    ????},
    ????
    ????getId: function() {
    ????????return 2;
    ????}
    });

    var TopClass = SubClass.extend({
    ????getName: function() {
    ????????//調(diào)用SubClass的getName()方法
    ????????return "TopClass(" + this.getId() + ") extends " +
    ????????????this.$.getName.call(this);
    ????},
    ????
    ????getId: function() {
    ????????//this.getId()總是返回調(diào)用父類的getId()方法的返回值(2)
    ????????return this.$.getId.call(this);
    ????}
    });

    //輸出結(jié)果:"TopClass(2) extends SubClass(2) extends BaseClass(2)"
    //一切都正確!
    alert(new TopClass().getName());

    ???????? 這里是前面示例的,但是目前這種方式包括了通過“extend”方法實現(xiàn)的十分清晰的類定義模式和正確的父類調(diào)用語義。尤其是“extend”方法通過一個中間function封裝了類定義中的每個方法,這個中間function在每次方法調(diào)用時首先把當前父類引用“$” 與正確的父類引用相互交換,然后把這個正確的父類引用傳遞給apply()進行方法調(diào)用,最后再將把當前父類引用“$” 與正確的父類引用交換回來。這種方式唯一的問題就是它需要一些中間function,它們會對性能產(chǎn)生不良影響。所以近來我重新審視了設(shè)計、完成了去掉了中間function了一種改良的方式。

    改良后的Solution:

    ????????動態(tài)父類調(diào)用快速版本:

    一次性支持代碼

    //定義最頂級類
    function Class() { }
    Class.prototype.construct = function() {};
    Class.extend = function(def) {
    ????var classDef = function() {
    ????????if (arguments[0] !== Class) { this.construct.apply(this, arguments); }
    ????};
    ????
    ????var proto = new this(Class);
    ????var superClass = this.prototype;
    ????
    ????for (var n in def) {
    ????????var item = def[n];????????????????????????
    ????????if (item instanceof Function) item.$ = superClass;
    ????????proto[n] = item;
    ????}

    ????classDef.prototype = proto;
    ????
    ????//賦給這個新的子類同樣的靜態(tài)extend方法
    ????classDef.extend = this.extend;????????
    ????return classDef;
    };


    運行示例:

    //Hey, 注意一下這個類的定義方式
    //看上去比其它方式要清楚些
    var BaseClass = Class.extend({
    ????construct: function() { /* optional constructor method */ },
    ????????
    ????getName: function() {
    ????????return "BaseClass(" + this.getId() + ")";
    ????},
    ????
    ????getId: function() {
    ????????return 1;
    ????}
    });

    var SubClass = BaseClass.extend({
    ????getName: function() {
    ????????//調(diào)用BaseClass的getName()方法
    ????????return "SubClass(" + this.getId() + ") extends " +
    ????????????arguments.callee.$.getName.call(this);
    ????},
    ????
    ????getId: function() {
    ????????return 2;
    ????}
    });

    var TopClass = SubClass.extend({
    ????getName: function() {
    ????????//調(diào)用SubClass的getName()方法
    ????????return "TopClass(" + this.getId() + ") extends " +
    ????????????arguments.callee.$.getName.call(this);
    ????},
    ????
    ????getId: function() {
    ????????// this.getId()總是返回調(diào)用父類的getId()方法的返回值(2)
    ????????return arguments.callee.$.getId.call(this);
    ????}
    });

    //輸出結(jié)果:"TopClass(2) extends SubClass(2) extends BaseClass(2)"
    //工作正常!而且沒有任何中間function
    alert(new TopClass().getName());

    ????????這是最后的設(shè)計,它使用了JavaScript中一點鮮為人知的特性:callee。

    ???????? 在任何方法執(zhí)行過程中,你可以查看那些通過“arguments”數(shù)組傳入的參數(shù),這是眾所周知的,但很少有人知道“arguments”數(shù)組包含一個名為“callee”的屬性,它作為一個引用指向了當前正在被執(zhí)行的function,而后通過“$”便可以方便的獲得當前被執(zhí)行function所在類的父類。這是非常重要的,因為它是獲得此引用的唯一途徑(通過“this”對象獲得的function引用總是指向被子類重載的function,而后者并非全是正在被執(zhí)行的function)。

    posted on 2006-12-31 15:55 捕風(fēng) 閱讀(5768) 評論(1)  編輯  收藏 所屬分類: web 頁面開發(fā)

    評論

    # re: javascript中子類調(diào)用父類方法解決方案 2008-12-30 20:03 長大不好玩

    我很欽佩你在這個問題上研究的深度,給我了很大的幫助,不過你說這句話時很讓我覺得惡心:
    "我很敬佩他在JavaScript編程方面做出的貢獻……"

      回復(fù)  更多評論   

    主站蜘蛛池模板: 无码天堂亚洲国产AV| 亚洲色av性色在线观无码| 亚洲愉拍一区二区三区| 2021国产精品成人免费视频| 亚洲欧洲高清有无| 黄页网站在线观看免费高清| 亚洲人成日本在线观看| 成人毛片18女人毛片免费视频未| 2020亚洲男人天堂精品| 免费视频中文字幕| 朝桐光亚洲专区在线中文字幕| 亚洲国产精品成人久久蜜臀 | 日本免费在线观看| 亚洲性天天干天天摸| 久久成人国产精品免费软件| 久久精品亚洲AV久久久无码| 免费观看一级毛片| 国产乱子伦精品免费视频| 亚洲国产综合精品中文第一区| 亚洲无砖砖区免费| 亚洲AV网一区二区三区| 不卡精品国产_亚洲人成在线| 亚洲免费精彩视频在线观看| 亚洲国产成人99精品激情在线| 日韩精品视频免费网址| 免费精品久久久久久中文字幕 | 中文字幕久无码免费久久| 香蕉视频在线观看亚洲| 免费无码A片一区二三区| 理论亚洲区美一区二区三区| 亚洲国产精品无码久久久不卡 | 亚洲成年人免费网站| 女bbbbxxxx另类亚洲| 亚洲AV无码国产在丝袜线观看| 久久国产乱子伦免费精品| 亚洲日韩AV一区二区三区四区| 国产精品亚洲高清一区二区| 国产h肉在线视频免费观看| 老司机午夜免费视频| 久久久久久亚洲精品中文字幕| 无人在线观看免费高清视频 |