-
js沒有傳統語言中的類式繼承,通過原型委託方式實現對象間的繼承
-
面向對象三大特性:抽象、繼承、多態
1.1 動態語言和鴨子類型
- 靜態類型語言在編譯時便已確定變量的類型,而動態類型語言的變量類型要到程序運行的時候,待變量被賦予某個值之後,纔會具有某種類型。
鴨子類型
“如果它走起路來像鴨子,叫起來也是鴨子,那麼它就是鴨子。”
-
只關注對象行爲,不關注對象本身。
-
故動態語言不必藉助超類型幫助,實現面向接口編程
1.2 多態
what:將“做什麼”和“誰去做以及怎麼去做”分離開
why:給予了我們擴展程序的能力,程序看起來是可生長的,也符合開放-封閉原則。
How:即將不變的事物和可能改變的分離開。
example 1:使動物叫
在例子中,不變的部分是動物都會發出叫的動作,會變的是,動物的叫聲不同。故程序可以分爲兩個部分。
function makeSound(animal){
animal.sound && animal.sound()
}
// 改變的部分
var Duck = function (){}
Duck.prototype.sound = function (){
console.log('gagaga')
}
var Dog = function(){}
Dog.prototype.sound = function(){
console.log('wangwangwang')
}
// 不變的部分
上文談到的多態實質上指的是對象的多態性。
Martin Fowler 在《重構:改善既有代碼的設計》裏寫到:
多態的最根本好處在於,你不必再向對象詢問“你是什麼類型”而後根據得到的答案調用對象的某個行爲——你只管調用該行爲就是了,其他的一切多態機制都會爲你安排妥當。
-
JavaScript 的變量類型在運行期是可變的。一個 JavaScript 對象,既可以表示 Duck 類型的對象,又可以表示 Chicken 類型的對象,這意味着 JavaScript 對象的多態性是與生俱來的。
-
多態最根本的作用就是通過把過程化的條件分支語句轉化爲對象的多態性,從而消除這些條件分支語句。
example 2:調用地圖Api
在本例子,會改變的是Api提供商,不變的是進行地圖渲染的動作
//使用不同地圖api進行渲染
//不變的是渲染行爲
var render = function(map){
if(map.show instanceof Function){
map.show()
}
}
//變的是 api提供者
var gooleMap = {
show:function(){
console.log('gooleMap進行渲染')
}
}
var baiduMap={
show:function(){
console.log('baiduMap進行渲染')
}
}
設計模型與多態
-
絕大部分設計模式的實現都離不開多態性的思想
-
在JavaScript中函數是一等對象,
1.3 封裝
封裝的目的是將信息隱藏。一般而言,我們討論的封裝是封裝數據和封裝實現。這一節將討論更廣義的封裝,不僅包括封裝數據和封裝實現,還包括封裝類型和封裝變化。
1.3.1 封裝數據
-
許多對象系統中,封裝數據由語法解析實現,例如private、public等
-
js依靠變量的作用域實現封裝特性,且只能模擬public和private
可使用let和函數來創建作用域
1.3.2 封裝實現
從封裝實現細節來講,封裝使得對象內部的變化對其他對象而言是透明的,也就是不可見的。
1.3.3 封裝類型
封裝類型是靜態類型語言中一種重要的封裝方式。一般而言,封裝類型是通過抽象類和接口來進行的。把對象的真正類型隱藏在抽象類或者接口之後
在Javascript中,沒有對抽象類和接口的支持,也沒有能力進行類型封裝。
1.3.4 封裝變化
《設計模式》一書曾提到如下文字:
“考慮你的設計中哪些地方可能變化,這種方式與關注會導致重新設計的原因相反。它不是考慮什麼時候會迫使你的設計改變,而是考慮你怎樣才能夠在不重新設計的情況下進行改變。這裏的關鍵在於封裝發生變化的概念,這是許多設計模式的主題。”
設計模式劃分爲創建型模式、結構性模式、行爲型模式
-
創建型模式: 創建對象是一種抽象行爲,即創建什麼對象是可以變化的。即封裝創建對象的變化
-
結構性模式: 封裝對象間的組合關係
-
行爲型模式:封裝對象的行爲變化
在系統演變中,只需替換可變的部分,因爲已經封裝好,故替換起來相對容易。最大程度地保證程序地穩定性和可擴展性