享元模式不一樣於通常的設計模式,它主要用來優化程序的性能,它最適合解決大量相似的對象而產生的性能問題。享元模式經過分析應用程序的對象,將其解析爲內在數據和外在數據,減小對象的數量,從而提升應用程序的性能。設計模式
享元模式經過共享大量的細粒度的對象,減小對象的數量,從而減小對象的內存,提升應用程序的性能。其基本思想就是分解現有相似對象的組成,將其展開爲能夠共享的內在數據和不可共享的外在數據,咱們稱內在數據的對象爲享元對象。一般還須要一個工廠類來維護內在數據。
在JS中,享元模式主要有下面幾個角色組成:
(1)客戶端:用來調用享元工廠來獲取內在數據的類,一般是應用程序所需的對象,
(2)享元工廠:用來維護享元數據的類
(3)享元類:保持內在數據的類iphone
咱們舉個例子進行說明:蘋果公司批量生產iphone,iphone的大部分數據好比型號,屏幕都是同樣,少數部分數據好比內存有分16G,32G等。未使用享元模式前,咱們寫代碼以下:函數
1 function Iphone(model, screen, memory, SN) { 2 this. model = model; 3 this.screen = screen; 4 this.memory = memory; 5 this.SN = SN; 6 } 7 var phones = []; 8 for (var i = 0; i < 1000000; i++) { 9 var memory = i % 2 == 0 ? 16 : 32; 10 phones.push(new Iphone("iphone6s", 5.0, memory, i)); 11 }
這段代碼中,建立了一百萬個iphone,每一個iphone都獨立申請一個內存。可是咱們仔細觀察能夠看到,大部分iphone都是相似的,只是內存和序列號不同,若是是一個對性能要求比較高的程序,咱們就要考慮去優化它。
大量類似對象的程序,咱們就能夠考慮用享元模式去優化它,咱們分析出大部分的iphone的型號,屏幕,內存都是同樣的,那這部分數據就能夠公用,就是享元模式中的內在數據,定義享元類以下:性能
1 function IphoneFlyweight(model, screen, memory) { 2 this.model = model; 3 this.screen = screen; 4 this.memory = memory; 5 }
咱們定義了iphone的享元類,其中包含型號,屏幕和內存三個數據。咱們還須要一個享元工廠來維護這些數據:優化
1 var flyweightFactory = (function () { 2 var iphones = {}; 3 return { 4 get: function (model, screen, memory) { 5 var key = model + screen + memory; 6 if (!iphones[key]) { 7 iphones[key] = new IphoneFlyweight(model, screen, memory); 8 } 9 return iphones[key]; 10 } 11 }; 12 })();
在這個工廠中,咱們定義了一個字典來保存享元對象,提供一個方法根據參數來獲取享元對象,若是字典中有則直接返回,沒有則建立一個返回。
接着咱們建立一個客戶端類,這個客戶端類就是修改自iphone類:this
1 function Iphone(model, screen, memory, SN) { 2 this.flyweight = flyweightFactory.get(model, screen, memory); 3 this.SN = SN; 4 }
而後咱們依舊像之間那樣生成多個iphonespa
1 var phones = []; 2 for (var i = 0; i < 1000000; i++) { 3 var memory = i % 2 == 0 ? 16 : 32; 4 phones.push(new Iphone("iphone6s", 5.0, memory, i)); 5 } 6 console.log(phones);
這裏的關鍵就在於Iphone構造函數裏面的this.flyweight = flyweightFactory.get(model, screen, memory)。這句代碼經過享元工廠去獲取享元數據,而在享元工廠裏面,若是已經存在相同數據的對象則會直接返回對象,多個iphone對象共享這部分相同的數據,因此本來相似的數據已經大大減小,減小的內存的佔用。設計
享元模式的一個典型應用就是DOM事件操做,DOM事件機制分紅事件冒泡和事件捕獲。咱們簡單介紹一下這二者:
事件冒泡:綁定的事件從最裏層的元素開始觸發,而後冒泡到最外層
事件捕獲:綁定的事件從最外層的元素開始觸發,而後傳到最裏層
假設咱們HTML中有一個菜單列表code
1 <ul class="menu"> 2 <li class="item">選項1</li> 3 <li class="item">選項2</li> 4 <li class="item">選項3</li> 5 <li class="item">選項4</li> 6 <li class="item">選項5</li> 7 <li class="item">選項6</li> 8 </ul>
點擊菜單項,進行相應的操做,咱們經過jQuery來綁定事件,通常會這麼作:對象
1 $(".item").on("click", function () { 2 console.log($(this).text()); 3 })
給每一個列表項綁定事件,點擊輸出相應的文本。這樣看暫時沒有什麼問題,可是若是是一個很長的列表,尤爲是在移動端特別長的列表時,就會有性能問題,由於每一個項都綁定了事件,都佔用了內存。可是這些事件處理程序其實都是很相似的,咱們就要對其優化。
1 $(".menu").on("click", ".item", function () { 2 console.log($(this).text()); 3 })
經過這種方式進行事件綁定,能夠減小事件處理程序的數量,這種方式叫作事件委託,也是運用了享元模式的原理。事件處理程序是公用的內在部分,每一個菜單項各自的文本就是外在部分。咱們簡單說下事件委託的原理:點擊菜單項,事件會從li元素冒泡到ul元素,咱們綁定事件到ul上,實際上就綁定了一個事件,而後經過事件參數event裏面的target來判斷點擊的具體是哪個元素,好比低級第一個li元素,event.target就是li,這樣就能拿到具體的點擊元素了,就能夠根據不一樣元素進行不一樣的處理。
享元模式是一種優化程序性能的手段,經過共享公用數據來減小對象數量以達到優化程序的手段。享元模式適用於擁有大量相似對象而且對性能有要求的場景。由於享元模式須要分離內部和外部數據,增長了程序的邏輯複雜性,建議對性能有要求的時候才使用享元模式。