這兩天在讀kissy的源代碼,從一開(kāi)始我就對(duì)它的mix()函數(shù)充滿了敵意。因?yàn)闊o(wú)論從哪個(gè)角度來(lái)看,那都是一個(gè)極其低效的實(shí)現(xiàn)。不過(guò)深入了解這個(gè)框架之后,我對(duì)kissy中的新的系統(tǒng)構(gòu)建的模型產(chǎn)生了興趣,而這種系統(tǒng)構(gòu)建的方式,也正是由mix()所帶來(lái)的。
一、對(duì)象系統(tǒng)
我們先了解一下對(duì)象系統(tǒng)。在《JavaScript語(yǔ)言精髓與編程實(shí)踐》中談到過(guò),面向?qū)ο笙到y(tǒng)有三種對(duì)象的繼承方式,即原型、類和元類。這三種方式都可以構(gòu)建大型對(duì)象系統(tǒng)。在后續(xù)討論之前,我們?cè)诿~概念上做一些強(qiáng)調(diào),所謂“對(duì)象系統(tǒng)”,是指由“一組對(duì)象構(gòu)成的系統(tǒng)”,這些對(duì)象之間存在或不存在某種聯(lián)系,但通過(guò)一些規(guī)則組織起來(lái)。所謂“面向?qū)ο笙到y(tǒng)”,是指以上述“對(duì)象系統(tǒng)”對(duì)基礎(chǔ)延伸演化的系統(tǒng),新系統(tǒng)滿足前對(duì)象系統(tǒng)的組織規(guī)則。
所謂對(duì)象系統(tǒng)的三個(gè)要素,即繼承、封裝與多態(tài),即是上述組織規(guī)則的要件。孟巖同學(xué)從C/C++出發(fā),從另一個(gè)側(cè)面談?wù)搶?duì)象系統(tǒng),所持的觀點(diǎn)我相當(dāng)認(rèn)可,這包括所述的“對(duì)象范式的基本觀念中不包括繼承、封裝與多態(tài)”——這一觀點(diǎn)有其確切的背景與思考方法,值得一談。
我們?cè)谶@里要討論的是“對(duì)象系統(tǒng)”,即,對(duì)象是如何組織起來(lái)的問(wèn)題。在這個(gè)問(wèn)題上,組織規(guī)則之一,就是“繼承”。JavaScript中基本的繼承模型是原型繼承,其特點(diǎn)是“新對(duì)象實(shí)例的特性,復(fù)制自一個(gè)原型對(duì)象實(shí)例”。Qomo以及其它的一些項(xiàng)目,通過(guò)語(yǔ)言擴(kuò)展的方式,在JavaScript上添加了類繼承的模型,其特點(diǎn)是“對(duì)象構(gòu)建自類,類是其父類的一個(gè)派生”,這里的“派生”與“特性復(fù)制”有潛在的關(guān)系,即:子類的特性也復(fù)制自父類。正是因?yàn)?#8220;派生”其實(shí)是特性復(fù)制的一種形式,所以事實(shí)上Qomo中的類繼承,是通過(guò)原型繼承來(lái)實(shí)現(xiàn)的,因?yàn)樵屠^承本質(zhì)上也就是“特性復(fù)制”。
無(wú)論是原型繼承、類繼承還是這里沒(méi)有進(jìn)一步討論的元類繼承,繼承的最終目的是構(gòu)建一個(gè)“對(duì)象系統(tǒng)”,而不是“系統(tǒng)”。這一個(gè)小小的措辭上的區(qū)別,有著本質(zhì)上的、深刻的意義,這也是我提及到孟巖的上一篇文章的原因。通常,由“繼承”入手理解的“對(duì)象系統(tǒng)”其實(shí)是靜態(tài)的,以至于我們面向?qū)ο笙到y(tǒng)開(kāi)發(fā)的最后一步,仍然需要框架來(lái)驅(qū)動(dòng)之。例如TApp.Run(),或者類似于new App(),等等。繼承所帶來(lái)的,主要仍然是指對(duì)象系統(tǒng)的組織性,而非其運(yùn)行過(guò)程中的動(dòng)態(tài)特性。于是我們通過(guò)更多類或其它對(duì)象系統(tǒng),來(lái)將一個(gè)系統(tǒng)的動(dòng)態(tài)特性靜態(tài)化,例如將對(duì)象之間的交互關(guān)系抽取出來(lái),變成控制類。我們做這些事情的目的,僅僅是因?yàn)槲覀兗s定了對(duì)象系統(tǒng)的組織規(guī)則,要面向這個(gè)對(duì)象系統(tǒng)開(kāi)發(fā),也必然滿足(或契合)這一組織規(guī)則。組織規(guī)則限定了我們構(gòu)建系統(tǒng)的方式——繼承、封裝與多態(tài),這在一定程度上說(shuō)是“對(duì)象系統(tǒng)構(gòu)建”的一個(gè)方案,并非“系統(tǒng)構(gòu)建”的方案。而孟巖在上文中討論的,也正是“系統(tǒng)構(gòu)建”的問(wèn)題。所以孟巖提出兩點(diǎn):
•程序是由對(duì)象組成的;
•對(duì)象之間互相發(fā)送消息,協(xié)作完成任務(wù)。
其第一條,是對(duì)象系統(tǒng)的基本特性,是謂系統(tǒng)成員;第二條,是對(duì)象系統(tǒng)如何演進(jìn)為系統(tǒng)的特性,是謂系統(tǒng)通訊。一個(gè)系統(tǒng)的約束,既包括其成員(以及成員的組織規(guī)則),也包括成員間的通訊。
二、用mix()來(lái)構(gòu)建系統(tǒng)
舍棄“繼承”這種方式不談,系統(tǒng)構(gòu)建還有其它的什么方法嗎?
kissy提供了另外一種可能性,即mix(),混合。在kissy系統(tǒng)的核心部分,為一個(gè)系統(tǒng)提出了三個(gè)概念:
1、原子(meta),一個(gè)系統(tǒng)具有至少一個(gè)原子,原子是具有mix()能力的一個(gè)對(duì)象;
2、宿主(host),一個(gè)系統(tǒng)有一個(gè)依賴的宿主,表明系統(tǒng)的外部環(huán)境,系統(tǒng)只是其宿主環(huán)境中的部分內(nèi)容,可以由特定的名稱來(lái)區(qū)別于其它;
3、種子(seed),一個(gè)系統(tǒng)誕生自一個(gè)種子,種子描述系統(tǒng)上述的meta和host兩個(gè)方面的特性。
kissy約定,一個(gè)系統(tǒng)誕生自一個(gè)種子,該種子通過(guò)不停地mix()而成長(zhǎng),變成一個(gè)復(fù)雜的系統(tǒng)。由種子培育成為系統(tǒng)的整個(gè)環(huán)境,只需要能夠理解mix與host,即可以基于seed來(lái)構(gòu)建任意復(fù)雜的系統(tǒng)。
上述的邏輯在kissy.js中,描述得相當(dāng)簡(jiǎn)單:
view plaincopy to clipboardprint?
(function(host, S) {
var meta = {
mix: function(r, s, ov, wl) {
...
}
},
// If KISSY is already defined, the existing KISSY object will not
// be overwritten so that defined namespaces are preserved.
seed = (host && host[S]) || {},
// The host of runtime environment. specify by user's seed or <this>,
// compatibled for '<this> is null' in unknown engine.
host = seed.__HOST || (seed.__HOST = host || {});
// shortcut and meta for seed.
S = host[S] = meta.mix(seed, meta, false);
return S;
})(this, 'KISSY');
(function(host, S) {
var meta = {
mix: function(r, s, ov, wl) {
...
}
},
// If KISSY is already defined, the existing KISSY object will not
// be overwritten so that defined namespaces are preserved.
seed = (host && host[S]) || {},
// The host of runtime environment. specify by user's seed or <this>,
// compatibled for '<this> is null' in unknown engine.
host = seed.__HOST || (seed.__HOST = host || {});
// shortcut and meta for seed.
S = host[S] = meta.mix(seed, meta, false);
return S;
})(this, 'KISSY');
這個(gè)系統(tǒng)初始化的時(shí)候,傳入host與host中的系統(tǒng)名S。對(duì)于kissy來(lái)說(shuō),host是當(dāng)前的系統(tǒng)環(huán)境,這里的this值,可以是javascript引擎的global,或?yàn)g覽器環(huán)境的window,或某個(gè)函數(shù)或?qū)ο箝]包內(nèi)的當(dāng)前this。而'KISSY'值,表現(xiàn)kissy系統(tǒng)在環(huán)境中的名字。按照javascript的語(yǔ)言約定,我們可以通過(guò)host[S]來(lái)找到既已經(jīng)存在的kissy系統(tǒng)。
按照此前的約定,一個(gè)mix構(gòu)建的系統(tǒng),必然有host和mix兩個(gè)性質(zhì),因?yàn)樗钤嫉姆N子(seed)就必然包括這兩種性質(zhì)。所以,既然我們上面是通過(guò)host[S]來(lái)訪問(wèn)一個(gè)“既已存在的kissy系統(tǒng)”,則無(wú)論該kissy系統(tǒng)經(jīng)過(guò)了何種程度的演化,必然會(huì)包括這兩種性質(zhì)。
上面的構(gòu)建過(guò)程嘗試尋找在host[S]中尋找這兩種性質(zhì),如果其中之一不存在,則嘗試初始化它。例如代碼:
view plaincopy to clipboardprint?
seed = (host && host[S]) || {},
seed = (host && host[S]) || {},
如果host[S]是存的,則假設(shè)它是一個(gè)seed,否則初始化為一個(gè)空的對(duì)象。接下來(lái):
view plaincopy to clipboardprint?
host = seed.__HOST || (seed.__HOST = host || {})
host = seed.__HOST || (seed.__HOST = host || {})
如果上述的種子seed有host屬性,則使用它既有的__HOST,如果沒(méi)有,則置為當(dāng)前環(huán)境下的host,或一個(gè)空的對(duì)象。
現(xiàn)在我們看到的seed,必然已經(jīng)具有host屬性。但是,它還“可能”缺少一個(gè)性質(zhì),即最最重要的mix()。mix()的作用其實(shí)很簡(jiǎn)單,就是從對(duì)象B將屬性抄寫(xiě)到對(duì)象A的一個(gè)方法。而這里,之所以說(shuō)是“可能”缺少,是因?yàn)槿绻鹲eed是既有的mix系統(tǒng),則他已經(jīng)有mix()屬性;如果它是第三方系統(tǒng),則可能沒(méi)有mix,或有一個(gè)不同的mix等等。下面的一行代碼嘗試用元語(yǔ)言的思想構(gòu)建它,即:
view plaincopy to clipboardprint?
meta.mix(seed, meta, false); // false值表明不覆蓋
meta.mix(seed, meta, false); // false值表明不覆蓋
元語(yǔ)言的特點(diǎn)是自描述的,meta.mix()可以向seed混入mix(),也可以使seed.mix()能混入其它系統(tǒng)或meta本身??傊趍ix()的構(gòu)建中,meta只需要有mix這個(gè)方法,不需要更多,也不能更少。
上一行代碼的結(jié)果,是:如果seed沒(méi)有自已的mix()屬性,則向seed混入meta的原始的mix()。
現(xiàn)在,我們?cè)倏磗eed,必然已經(jīng)具有了host和mix()屬性。它本身可能是一個(gè)空對(duì)象,也可能是一個(gè)龐大的既有系統(tǒng),但無(wú)論如此,它具有了這兩個(gè)性質(zhì),就可以作為seed進(jìn)一步的衍生。
在這一切之前,下面的代碼保證它位于HOST[S],并返回這個(gè)系統(tǒng):
view plaincopy to clipboardprint?
S = host[S] = meta.mix(seed, meta, false);
return S;
S = host[S] = meta.mix(seed, meta, false);
return S;
三、mix()系統(tǒng)構(gòu)建中的其它概念
kissy除了實(shí)現(xiàn)基本的mix系統(tǒng)之外,在core部分加入了其它的一些功能。包括除mix()之外的兩種混入方法:
- augment,擴(kuò)充。用mix方法,將另一些子系統(tǒng)s[i]的原型,混入目標(biāo)子系統(tǒng)r的原型。
- merge,合并。用mix方法,將另一些子系統(tǒng)s[i],混入當(dāng)前子系統(tǒng)S。
基本上來(lái)說(shuō),augment是通過(guò)mix來(lái)對(duì)javascript的原型系統(tǒng)進(jìn)行擴(kuò)充的方法,或是在應(yīng)用系統(tǒng)中,結(jié)合原型機(jī)制與混入機(jī)制來(lái)構(gòu)建系統(tǒng)。而merge只是mix方法的一個(gè)批量工具。
另外,考慮到面向?qū)ο笙到y(tǒng)中的繼承特性,kissy也實(shí)現(xiàn)了extend(派生)方法,以提供傳統(tǒng)的面向?qū)ο缶幊棠芰Α?/div>
除了語(yǔ)言級(jí)別的概念之外,kissy也提供系統(tǒng)框架級(jí)別的一些構(gòu)建能力。包括:
- app,應(yīng)用。與host[S]并列的,具有同等能力的其它應(yīng)用,app('XXX', ...)可以在host['XXX']上組織應(yīng)用。
- namespace,命名空間。即可以組織出host[XXX].YYY.ZZZ這樣的,在不同子系統(tǒng)中的,不同命名空間下的系統(tǒng)。
最后,kissy在內(nèi)核中也提供簡(jiǎn)單的調(diào)試支持。
顯然的,基于mix的原則,任何一個(gè)第三方的系統(tǒng)可以通過(guò)混入kissy來(lái)修改上述的概念,例如覆蓋extend()來(lái)實(shí)現(xiàn)自己的對(duì)象系統(tǒng)構(gòu)建原則,或覆蓋app()來(lái)實(shí)現(xiàn)自己的應(yīng)用組織原則。第三方系統(tǒng)也可以將kissy混入自身,在保障自身特性的情況下,使用kissy,以及更大規(guī)模的kissy ui系統(tǒng)帶來(lái)的好處。
四、一點(diǎn)點(diǎn)提示
kISSY是什么?
KISSY是一個(gè)開(kāi)源的javascript項(xiàng)目,其主體是一個(gè)前端UI開(kāi)發(fā)框架,即KissyUI。本文所述的kissy是僅指其內(nèi)核部分的kissy.js中的語(yǔ)言與框架設(shè)計(jì)思想。KISSY項(xiàng)目的開(kāi)源網(wǎng)站是:
http://kissyteam.github.com/kissy怎么使用呢?
盡管在KissyUI向kissy內(nèi)核化的過(guò)程中,我們提出了一些新的概念與框架模型,但事實(shí)上,我們并未改變KissyUI的任何使用慣例。從代碼上來(lái)看,kissy.js和lang.js以后的其它模塊,并沒(méi)有任何的變化,因此如果僅是將kissy當(dāng)成一個(gè)UI系統(tǒng)來(lái)使用,你可以參考上面的開(kāi)源網(wǎng)站,其中既有的KissyUI文檔是完全有效的,而且KissyUI本身也是一個(gè)優(yōu)秀的、便捷的Web UI框架。但是,kissy系統(tǒng)在模向合并和組織上的能力大大增強(qiáng)了。例如說(shuō),我們可以開(kāi)始想象下面這樣的代碼:
view plaincopy to clipboardprint?
<!-- 先裝載jQuery -->
<script src="jQuery.js" mce_src="jQuery.js"></script>
<!-- 將jQuery系統(tǒng)映射到KISSY,作為初始的seed -->
<script type="text/javascript">
window.KISSY = jQuery;
</script>
<!-- 裝載kissy,在jQuery上混入KISSY -->
<script src="kissy.js"></script>
<script type="text/javascript">
document.writeln('你現(xiàn)在使用的是Kissy,還是jQuyer?答案請(qǐng)選Y,或者Y');
document.writeln('你現(xiàn)在能裝載Kissy UI,還是jQuery UI?答案請(qǐng)選Y,或者Y');
KISSY.merge(YUI, Dojo, Qomo).merge(Biby);
document.writeln("what's KISSY? select collapsar or black hole, pls.");
</script>
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)
點(diǎn)擊舉報(bào)。