[1]對象 [2]類 [3]封裝 [4]聚合 [5]繼承 [6]多態 ...
前面的話
面向對象描述了一種代碼的組織結構形式——一種在軟體中對真實世界中問題領域的建模方法。本文將從理論層面,介紹javascript面向對象程式程式(OOP)中一些常見的概念
對象
所謂對象,本質上就是指事物(包括人和物)在程式設計語言中的表現形式。這裡的事物可以是任何東西(如某個客觀存在的對象,或者某些較為抽象的概念)。例如,對於貓這種常見對象來說,具有某些明確的特征(如顏色、名字、體型等),能執行某些動作(如喵喵叫、睡覺、躲起來、逃跑等)。在OOP語義中,這些對象特征都叫做屬性,而那些動作則被稱為方法
此外,還有一個口語方面的類比:對象往往是用名詞表示的(如book、person),方法一般都是些動詞(如read、run),屬性值則往往是一些形容詞
“The black cat sleeps on my head”
cat是一個對象,black是一個顏色屬性值,sleep代表一個動作,也就是OOP語義中的方法。on my head是動作sleep的限定條件。因此它可以當做傳遞給sleep方法的一個參數
類
在現實生活中,相似對象之間往往都有一些共同的組成特征。例如蜂鳥和老鷹都具有鳥類的特征,因此它們可以被統稱為鳥類。在OOP中,類實際上就是對象的設計藍圖或製作配方。對象這個詞,有時候也叫做實例。所以,老鷹是鳥類的一個實例。可以基於同一個類創建出許多不同的對象,因為類更多的是一種模板。而對象則是在這些模板的基礎上被創建出來的實體
javascript實際上壓根沒有類。該語言的一切都是基於對象的,其依靠的是一套原型(prototype)系統。而原型本身實際上也是一種對象
在傳統的面向對象語言中,基於Person類創建了一個Match的新對象,而在javascript中,則是將現有的Person對象擴展成一個Match的新對象
封裝
封裝主要用於闡述對象中所包含的內容。封裝概念通常由兩部分組成:相關的數據(用於存儲屬性)、基於這些數據所能做的事(所能調用的方法)
封裝的目的是將信息隱藏,即方法與屬性的可見性。一般而言,封裝包括封裝數據和封裝實現
在許多語言的對象系統中,封裝數據是由語法解析來實現的,這些語言提供了public、private、protected這些關鍵字來限定方法和屬性的可見性,這種限定分類定義了對象用戶所能訪問的層次
但javascript並沒有提供對這些關鍵字的支持,只能依賴變數的作用域來實現封裝特性, 而且只能模擬出 public 和 private 這兩種封裝性。除了ECMAScript6中提供的let之外,一般通過函數來創建作用域:
var myObject = (function(){ var name = 'match'; // 私有(private)變數 return { getName: function(){ // 公開(public)方法 return name; } } })(); console.log( myObject.getName() );// 輸出:match console.log( myObject.name ) // 輸出:undefined
面向對象編程強調的是數據和操作數據的行為本質上是互相關聯的,因此好的設計就是把數據以及和它相關的行為封裝起來。舉例來說,用來表示一個單詞或者短語的一串字元通常被稱為字元串。字元就是數據。但是關心的往往不是數據是什麼,而是可以對數據做什麼,所以可以應用在這種數據上的行為(計算長度、添加數據、搜索等等)都被設計成 String 類的方法。所有字元串都是 String 類的一個實例,也就是說它是一個包裹,包含字元數據和可以應用在數據上的函數
封裝不僅僅是隱藏數據,還包括隱藏實現細節、設計細節以及隱藏對象的類型等
從封裝實現細節來講,封裝使得對象內部的變化對其他對象而言是透明的,也就是不可見的。 對象對它自己的行為負責。其他對象或者用戶都不關心它的內部實現。封裝使得對象之間的耦合變鬆散,對象之間只通過暴露的 API 介面來通信。當修改一個對象時,可以隨意地修改它的內部實現,只要對外的介面沒有變化,就不會影響到程式的其他功能
聚合
所謂聚合,有時候也叫做組合,實際上是指將幾個現有對象合併成一個新對象的過程。總之,這個概念強調的是將多個對象合而為一的能力。通過聚合這種強有力的方法,可以將一個問題分解成多個更小的問題。這樣一來,問題就會顯得更易於管理(便於各個擊破)。當一個問題域的太過複雜時,就可以考慮將它分解成若幹個子問題區,並且必要的話,這些問題區還可以再繼續分解成更小的分區。這樣做有利於從幾個不同的抽象層次來考慮這個問題
類似的情況如Book是由一個或多個author對象,publisher對象、若幹chapter對象以及一組table對象等組合而成的對象
繼承
通過繼承這種方法,可以非常優雅地實現對現有代碼的重用。在傳統的OOP環境中,繼承通常指的是類與類之間的關係,但由於javascript中不存在類,因此它的繼承只能發生在對象之間
比如,有一個Person的一般性對象,其中包含一些姓名、性別之類的屬性,以及一些功能性函數,如步行、談話、睡覺、吃飯等。然後,需要一個Programmer對象時,可以讓Programmer繼承自Person,Programmer對象只需要實現屬於它自己的那部分特殊功能(如編寫代碼),而其餘部分重用Person的實現即可
當一個對象繼承自另一個對象時,通常會往其中加入新的方法,以擴展被繼承的老對象。通常將這一過程稱之為“B繼承自A”或“B擴展自A”。另外對於新對象來說,它可以根據自己的需要,從繼承的那組方法中選擇幾個來重新定義。這樣做並不會改變對象的介面,因為其方法名是相同的,只不過當調用新對象時,該方法的行為與之前不同了。這種重定義繼承方法的過程叫做覆寫
多態
多態一詞源於希臘文polymorphism,拆開來看是poly(複數)+morph(形態)+ism,從字面意思可以理解為複數形態
多態的實際含義是:同一個操作作用於不同的對象上面,可以產生不同的解釋和不同的執行結果。換句話說,給不同的對象發送同一個消息的時候,這些對象會根據這個信息分別給出不同的反饋
Programmer對象繼承了上一級對象Person的所有方法。這意味著這兩個對象都實現了"talk"等方法。現在,代碼中有一個叫做“Match”的變數,即使是在不知道它是一個Person對象還是一個Programmer對象的情況下,也依然可以直接調用該對象的"talk"方法,而不必擔心這會影響代碼的正常工作。類似這種不同對象通過相同的方法調用來實現各自行為的能力,稱之為多態
多態背後的思想是將“做什麼”和“誰去做以及怎樣去做”分離開來,也就是將“不變的事物”與“可能改變的事物”分離開來。把不變的部分隔離出來,把可變的部分封裝起來,這給予了我們擴展程式的能力,程式看起來是可生長的,也符合開放——封閉原則,相對於修改代碼來說,僅僅增加代碼就能完成同樣的功能,這顯然優雅和安全得多
多態最根本的作用是通過把過程化的條件分支語句轉化為對象的多態性,從而消除這些條件分支語句
總結
下麵來對上面提到的概念進行總結
對象:Match是一個男人(後者是一個對象)
屬性:Match是男性,黃皮膚,黑頭髮
方法:Match能吃飯、睡覺、喝水、做夢
類:Match是Programmer類的一個實例
原型對象:Match是一個由Programmer對象擴展而來的新對象
封裝:Match對象包含了數據和基於這些數據的方法
聚合:Match只是整個Web開發團隊對象的一部分,該團隊還包括一個Designer對象Wang,以及一個ProjectManager對象Li
繼承:Designer、ProjectManager、Programmer都是分別擴展自Person對象的新對象
多態:可以隨時調用Match、Wang、Li這三個對象各自的talk方法,它們都可以正常工作,儘管這些方法會產生不同的結果。如Match可能談得更多的是代碼的性能,Wang更傾向於談代碼的優雅性,而Li強調的是最後期限。總之,每個對象都可以重新自定義它們的繼承方法talk