封裝的目的是將信息隱藏。通常而言,咱們討論的封裝是封裝數據和封裝實現。真正的封裝爲更廣義的封裝,不只包括封裝數據和封裝實現,還包括封裝類型和封裝變化。javascript
在許多語言的對象系統中,封裝數據是由語法解析來實現的,這些語言也許提供了 private、public、protected 等關鍵字來提供不一樣的訪問權限。但JavaScript並無提供對這些關鍵字的支持,咱們只能依賴變量的做用域來實現封裝特性,並且只能模擬出 public 和 private 這兩種封裝性。java
除了 ECMAScript 6 中提供的 let 以外,通常咱們經過函數來建立做用域:設計模式
var myObject = (fucntion(){ var __name = 'sven'; // 私有(private變量) return { getname: funvtion (){ // 公開(public)方法 return __name; } } })(); console.log(myObject.getName()); // 輸出: sven console.log(myObject.__name); // 輸出 undefined
外值得一提的是,在 ECAMScript 6 中,還能夠經過 Symbol 建立私有屬性。函數
封裝的目的是將信息隱藏,封裝應該被視爲「任何形式的封裝」,也就是說,封裝不只僅是隱藏數據,還包括隱藏實現細節、設計細節以及隱藏對象的類型等。設計
從封裝實現細節來說,封裝使得對象內部的變化對其餘對象而言是透明的,也就是不可見的。對象對它本身的行爲負責。其餘對象或者用戶都不關心它的內部實現。封裝使得對象之間的耦合變鬆散,對象之間只經過暴露的API接口來通訊。當咱們修改一個對象時,能夠隨意地修改它的內部實現,只要對外的接口沒有變化,就不會影響到程序的其餘功能。code
封裝實現細節的例子很是之多。拿迭代器來講明,迭代器的做用是在不暴露一個聚合對象的內部表示的前提下,提供一種方式來順序訪問這個聚合對象。咱們編寫了一個each函數,它的做用就是遍歷一個聚合對象,使用這個 each 函數的人不用關心它的內部是怎樣實現的,只要它提供的功能正確即可以。即便each函數修改了內部源代碼,只要對外的接口或者調用方式沒有變化,用戶就不用關心它內部實現的改變。對象
封裝類型是靜態類型語言中一種重要的封裝方式。通常而言,封裝類型是經過抽象類和接口來進行的。把對象的真正類型隱藏在抽象類或者接口以後,相比對象的類型,客戶更關心對象的行爲。在許多靜態語言的設計模式中,千方百計地去隱藏對象的類型,也是促使這些模式誕生的緣由之一。好比工廠方法模式、組合模式等。固然在 JavaScript 中,並無對抽象類和接口的支持。 JavaScript自己也是一門類型模糊的語言。在封裝類型方面, JavaScript 沒有能力,也沒有必要作得更多。對於JavaScript的設計模式實現來講,不區分類型是一種失色,也能夠說是一種解脫。接口
從設計模式的角度出發,封裝在更重要的層面體現爲封裝變化。
《設計模式》一書曾提到以下文字:ip
「考慮你的設計中哪些地方可能變化,這種方式與關注會致使從新設計的緣由相反。它不是考慮何時會迫使你的設計改變,而是考慮你怎樣纔可以在不從新設計的狀況下進行改變。這裏的關鍵在於封裝發生變化的概念,這是許多設計模式的主題。」作用域
這段文字便是《設計模式》提到的「找到變化並封裝之」。《設計模式》一書中共概括總結了23種設計模式。從意圖上區分, 這 23 種設計模式分別被劃分爲:
建立型模式: 建立一個對象是種抽象行爲,具體建立什麼對象是能夠變化的,建立型模式的目的是封裝建立對象的變化。
結構型模式: 封裝的是對象之間的組合關係。
行爲型模式: 封裝的是對象的行爲變化。
經過封裝變化的方式,把系統中穩定不變的部分和容易變化的部分隔離開來,在系統的演變過程當中,咱們只須要替換那些容易變化的部分,若是這些部分是已經封裝好的,替換起來也相對容易。這能夠最大程度地保證程序的穩定性和可擴展性。
從《設計模式》副標題「可複用面向對象軟件的基礎」能夠知道,這本書理應教咱們如何編寫可複用的面向對象程序。這本書把大多數筆墨都放在如何封裝變化上面,這跟編寫可複用的面向對象程序是不矛盾的。當咱們想辦法把程序中變化的部分封裝好以後,剩下的便是穩定而可複用的部分了。