原文[ https://dzone.com/articles/is-javascript-a-true-oop-language ]
我知道,這個(gè)話題已經(jīng)被討論過(guò)無(wú)數(shù)次,但是,它是當(dāng)前總會(huì)被提及的話題。每當(dāng)一個(gè)使用Java或C#或者其他面向?qū)ο箝_(kāi)發(fā)語(yǔ)言的開(kāi)發(fā)者接觸JavaScript的時(shí)候,他總會(huì)抱怨,會(huì)說(shuō)JavaScript太混亂、沒(méi)有類型、結(jié)構(gòu)也不好,還有很多奇奇怪怪的地方,它的對(duì)象支持也是微乎其微,因此他絕對(duì)不是一個(gè)面向?qū)ο缶幊痰恼Z(yǔ)言。
有些抱怨還是可以接受的,但有一部分確實(shí)帶有偏見(jiàn)。例如JavaScript沒(méi)有類型的聲明,那么它就不是OOP語(yǔ)言。關(guān)于后一點(diǎn),在確認(rèn)JavaScript是不是面向?qū)ο缶幊讨埃銘?yīng)該問(wèn)一下你自己:是什么讓一門編程語(yǔ)言成為面向?qū)ο蟮木幊陶Z(yǔ)言。
什么是OOP
OOP模式不是基于正式的標(biāo)準(zhǔn)規(guī)范,沒(méi)有一個(gè)技術(shù)文檔定義了OOP它是什么,它不是什么。OOP定義主要是基于早期研究人員發(fā)表的論文中的常識(shí),如Kristen Nygaard, Alan Kays, William Cook等人。已經(jīng)有很多嘗試去定義OOP和普遍被接受的定義方式對(duì)編程語(yǔ)言按照面向?qū)ο筮M(jìn)行分類,主要是基于兩點(diǎn)要求:
- 它具有以對(duì)象形式將一個(gè)問(wèn)題進(jìn)行建模的能力。
- 它對(duì)于編程中模塊化和代碼重用原則的支持度。
為了滿足第一個(gè)要求,一個(gè)語(yǔ)言必須允許開(kāi)發(fā)者使用對(duì)象來(lái)描述現(xiàn)實(shí)并定義對(duì)象之間的關(guān)系,如下所示:
- 關(guān)聯(lián):對(duì)象引用另一個(gè)獨(dú)立對(duì)象的能力。
- 聚合:對(duì)象嵌入一個(gè)或多個(gè)獨(dú)立對(duì)象的能力。
- 組合:對(duì)象嵌入一個(gè)或多個(gè)依賴對(duì)象的能力。
一般來(lái)說(shuō),如果語(yǔ)言支持以下原則,則滿足第二個(gè)要求:
- 封裝:這是集中在單個(gè)實(shí)體的數(shù)據(jù)和操縱它的代碼,隱藏其內(nèi)部細(xì)節(jié)的能力。
- 繼承:這是一種對(duì)象從一個(gè)或多個(gè)其他對(duì)象獲取某些或所有要素的機(jī)制。
- 多態(tài):這是根據(jù)數(shù)據(jù)類型或結(jié)構(gòu)不同地處理對(duì)象的能力。
符合這些要求的,通常我們就可以把一個(gè)編程語(yǔ)言分類為面向?qū)ο蟆?/p>
JavaScript & OOP
現(xiàn)在我們知道一個(gè)OOP語(yǔ)言是是什么樣子的了,那么我們能夠證明JavaScript是一個(gè)OOP語(yǔ)言么?讓我們來(lái)試一下吧
接下來(lái),展示一下JavaScript對(duì)象實(shí)現(xiàn)關(guān)聯(lián)、聚合、組合是很輕松的。
請(qǐng)參考以下代碼:
var johnSmith = {
firstName: "John",
lastName: "Smith",
address: { //Composition **組合**
street: "123 Duncannon Street",
city: "London",
country: "United Kingdom"
}
};
var nickSmith = {
firstName: "Nick",
lastName: "Smith",
address: { //Composition **組合**
street: "321 Oxford Street",
city: "London",
country: "United Kingdom"
}
};
johnSmith.parent = nickSmith; //Association **關(guān)聯(lián)**
var company = {
name: "ACME Inc.",
employees: []
};
//Aggregation **聚合**
company.employees.push(johnSmith);
company.employees.push(nickSmith);
您可以從上面代碼中找到一個(gè)組合(address屬性)的示例,一個(gè)關(guān)聯(lián)(parent 屬性)的示例和一個(gè)聚合示例(employees屬性)。
封裝,JavaScript對(duì)象是擁有數(shù)據(jù)和方法的實(shí)例,但它們沒(méi)有高級(jí)原生方法隱藏內(nèi)部細(xì)節(jié)。 JavaScript對(duì)象不關(guān)心隱私。 一不小心,可能導(dǎo)致所有屬性和方法都可以公開(kāi)訪問(wèn)。但是,我們可以通過(guò)一些技術(shù)來(lái)定義對(duì)象的內(nèi)部狀態(tài),防止對(duì)象的外部訪問(wèn):通過(guò)使用getter和setter來(lái)利用閉包。
通過(guò)所謂的原型繼承,JavaScript從它最基本層次上支持了繼承。 即使一些開(kāi)發(fā)人員認(rèn)為它有點(diǎn)簡(jiǎn)單,但JavaScript的繼承機(jī)制是完全有效的,也讓您獲得與大多數(shù)公認(rèn)的OOP語(yǔ)言相同的結(jié)果。 無(wú)論如何,JavaScript都有一個(gè)機(jī)制:“一個(gè)對(duì)象從一個(gè)或多個(gè)其他對(duì)象獲取一些或所有的功能”,這是繼承。
如何讓JavaScript具有多態(tài)性,似乎讓挑戰(zhàn)更加困難了,因?yàn)樵S多人把這個(gè)概念與數(shù)據(jù)類型聯(lián)系起來(lái)。 實(shí)際上,多態(tài)性涉及編程語(yǔ)言的許多方面,它不僅與OOP語(yǔ)言有關(guān)。 通常它涉及很多項(xiàng),例如泛型,重載和結(jié)構(gòu)子類型。 所有這些東西對(duì)于一個(gè)JavaScript這種“簡(jiǎn)單”和弱類型的語(yǔ)言來(lái)說(shuō)似乎太多了。然而并不是:在JavaScript中,我們可以通過(guò)幾種方式實(shí)現(xiàn)不同類型的多態(tài),也許我們寫代碼的時(shí)候在不知不覺(jué)中已經(jīng)做了很多次。
沒(méi)有 類 的OOP
"好吧,但是JavaScript沒(méi)有類啊" (反對(duì)JavaScript為OOP的語(yǔ)氣說(shuō))
許多開(kāi)發(fā)者因?yàn)镴avaScript缺乏類的概念而沒(méi)有把它看作一種真正面向?qū)ο笳Z(yǔ)言,因?yàn)樗环螼OP的硬性要求。
但是,我們可以看到,我們的非正式定義中沒(méi)有明確的要求具備類。 特性和原理才是對(duì)象所明確要求具備的。 類不是真正必須具備的,但它們有時(shí)是一種很方便的方法,讓我們可以抽象出具有公共屬性的對(duì)象。 因此,如果像JavaScript這樣的語(yǔ)言,即使沒(méi)有類,只要支持對(duì)象,那么也是面向?qū)ο蟮恼Z(yǔ)言。
PS:ES6讓JavaScript越來(lái)越強(qiáng)健,作者說(shuō)的一些缺點(diǎn),在ES6中慢慢都有補(bǔ)充。