基于對(duì)象還是面向?qū)ο螅ǘ?/h2>

面向?qū)ο蟪绦蛟O(shè)計(jì)理念

面向?qū)ο蟪绦蛟O(shè)計(jì)并非是面向?qū)ο缶幊陶Z言出現(xiàn)才有的概念,實(shí)際上早在這之前人們就在各種編程語言中實(shí)踐著面向?qū)ο蟪绦蛟O(shè)計(jì)。比如很典型的C語言中的文件讀寫函數(shù),代碼如下:

int fh = fopen(filepath);
fread(fh, buff, length);
fclose(fh);

fopen會(huì)在內(nèi)核中創(chuàng)建一個(gè)文件對(duì)象,也可以稱之為內(nèi)核對(duì)象,并且返回一個(gè)唯一id,這個(gè)id我們稱之為文件句柄。所有的文件讀寫操作都必須傳入一個(gè)有效的文件句柄。我們仔細(xì)分析這一段代碼,不難發(fā)現(xiàn),這已經(jīng)就是面向?qū)ο缶幊塘恕km然在語言層面,這種寫法和后來的面向?qū)ο笳Z言還有較大差別,但在運(yùn)行時(shí)的數(shù)據(jù)結(jié)構(gòu)方面,和后來的高級(jí)語言所生成的數(shù)據(jù)結(jié)構(gòu)完全一致。人們發(fā)現(xiàn)運(yùn)用面向?qū)ο蟪绦蛟O(shè)計(jì),能提高代碼的可讀性和可重用性,特別適合開發(fā)大型應(yīng)用,尤其適合對(duì)大型應(yīng)用進(jìn)行拆解,對(duì)其進(jìn)行模塊化開發(fā),這就大大地提高了開發(fā)效率?;蛘呶覀兛梢杂靡痪湓捀爬ǎ怯捎诿嫦?qū)ο笳Z言的出現(xiàn),才使普通程序員也能輕松駕馭大型企業(yè)級(jí)開發(fā)。這在以前是無法做到的。也由此誕生了“碼農(nóng)”這個(gè)概念,意為專業(yè)水平不高但也能勝任開發(fā)工作的程序員,這在以前是沒有的。

基于原型鏈的面向?qū)ο蟪绦蛟O(shè)計(jì)

盡管面向?qū)ο蟪绦蛟O(shè)計(jì)大大地提高了程序開發(fā)效率,但是在一些專業(yè)人士看來,它也并非是完美無缺。比如JavaScript的發(fā)明者就是這么認(rèn)為的。當(dāng)公司要求仿照J(rèn)ava設(shè)計(jì)一個(gè)在網(wǎng)頁客戶端運(yùn)行的用以驗(yàn)證客戶輸入是否正確的腳本時(shí),他琢磨來琢磨去,覺得作為一門腳本語言,如果完全照搬Java的設(shè)計(jì)理念,顯然太重了。另一面,Java對(duì)于函數(shù)的可復(fù)用性也并沒有達(dá)到他理想的預(yù)期。我們不妨寫一段偽代碼,開看看為何Java對(duì)于函數(shù)的可復(fù)用性還有哪些不足之處。代碼如下:

class A{
    public void Show()
    {
    }
}
class B : A
{}
class C : A
{}
B b = new B();
C c = new C();
b.Show();
c.Show();

在上面的代碼中,b.Show()和c.Show()顯然不是同一個(gè)Show。也就是說,在內(nèi)存中,存在兩份Show的拷貝,如果我們new出更多對(duì)象,Show的副本也會(huì)隨之增加。在JavaScript作者眼中,這顯然是不合理的,他認(rèn)為在自己新設(shè)計(jì)的腳本語言中,不應(yīng)該出現(xiàn)這樣的情況,他認(rèn)為在內(nèi)存中,只應(yīng)該存在一份Show的實(shí)例,其他所有對(duì)Show的調(diào)用都是對(duì)該實(shí)例的引用。為此,他專門設(shè)計(jì)了原型鏈這樣一個(gè)數(shù)據(jù)結(jié)構(gòu),并且果斷拋棄了面向?qū)ο笾蓄惖母拍?。在他看來,只有拋棄類,才能更大程度地?fù)用代碼。在這里,我們用原型鏈來實(shí)現(xiàn)一下對(duì)Show的復(fù)用。代碼如下:

var a = {
    Show:function(){}
}
var b = Object.create(a);
var c = Object.create(a);
var d = Object.create(a);
b.Show();
c.Show();
d.Show();

在上述代碼中,不管你基于a創(chuàng)建多少個(gè)實(shí)例,始終都只存在一份Show的副本。在JavaScript作者看來,這才是更為理想的代碼復(fù)用。在基于原型鏈的面向?qū)ο笾?,所有的?duì)象都掛載在一個(gè)樹形結(jié)構(gòu)上,根節(jié)點(diǎn)為一個(gè)Object類的實(shí)例,所有該樹結(jié)構(gòu)上的節(jié)點(diǎn)對(duì)Objcet上的方法調(diào)用都是引用根節(jié)點(diǎn)。例外的是Object.create(null),它單獨(dú)存在,我們也可以說它和根節(jié)點(diǎn)平級(jí)。將上述代碼和面向?qū)ο蟠a仔細(xì)對(duì)面,我們不難發(fā)現(xiàn),基于原型鏈的面向?qū)ο髮?shí)際上是面向?qū)ο蟮囊粋€(gè)閹割版。原型鏈這樣一個(gè)結(jié)構(gòu)我們完全可以在面向?qū)ο缶幊讨杏猛瑯拥逆準(zhǔn)浇Y(jié)構(gòu)進(jìn)行實(shí)現(xiàn),也能達(dá)到一樣的復(fù)用效果。但是如果我們?cè)谠玩湹幕A(chǔ)上去實(shí)現(xiàn)面向?qū)ο蟮奶匦?,就?huì)發(fā)現(xiàn)代碼看上去非常別扭,許多知名的JavaScript專家都極力不推薦這樣做。
作者為什么要這么做?那么,我們?cè)倩仡櫼幌律衔牡恼撌鼍筒浑y理解了。由于作者所設(shè)計(jì)的是一門腳本語言,而并非一門編譯語言,因此它肯定要足夠輕量,否則會(huì)加重解釋器的負(fù)擔(dān)。為此,作者專門設(shè)計(jì)了原型鏈來最大限度地復(fù)用代碼。那么,到此為止,對(duì)于“函數(shù)是一等公民”,你是否又有了新的認(rèn)知呢?或許我們也可以換一種講法,就是“一個(gè)函數(shù),只有一個(gè)副本”,你是否也這樣認(rèn)為呢?
好了,就到這里吧,希望你有所收獲。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容