享元模式最適合解決因創建大量類似物件而累及效能的問題,本文將來總結JavaScript設計模式程式設計中的享元模式使用,包括在DOM操作時的利用範例:
享元模式不同於一般的設計模式,它主要用來優化程式的效能,它最適合解決大量類似的物件而產生的效能問題。享元模式透過分析應用程式的對象,將其解析為內在數據和外在數據,減少對象的數量,從而提高應用程式的效能。
基本知識
享元模式透過共享大量的細粒度的對象,減少對象的數量,從而減少對象的內存,提高應用程式的效能。其基本思想就是分解現有類似對象的組成,將其展開為可以共享的內在數據和不可共享的外在數據,我們稱內在數據的對象為享元對象。通常還需要一個工廠類別來維護內在資料。
在JS中,享元模式主要有下面幾個角色組成:
(1)客戶端:用來呼叫享元工廠來取得內在資料的類,通常是應用程式所需的對象,
(2)享元工廠:用來維護享元資料的類別
(3)享元類別:保持內在資料的類別
享元模式的實作與應用
一般實作
我們舉個例子來說明:蘋果公司大量生產iphone,iphone的大部分數據例如型號,螢幕都是一樣,少數部分數據例如記憶體有分16G,32G等。未使用享元模式前,我們寫程式碼如下:
function Iphone(model, screen, memory, SN) { this. model = model; this.screen = screen; this.memory = memory; this.SN = SN; } var phones = []; for (var i = 0; i < 1000000; i++) { var memory = i % 2 == 0 ? 16 : 32; phones.push(new Iphone("iphone6s", 5.0, memory, i)); }
#這段程式碼中,創建了一百萬個iphone,每個iphone都獨立申請一個記憶體。但是我們仔細觀察可以看到,大部分iphone都是類似的,只是記憶體和序號不一樣,如果是一個對效能要求比較高的程序,我們就要考慮去優化它。
大量相似物件的程序,我們就可以考慮用享元模式去優化它,我們分析出大部分的iphone的型號,屏幕,內存都是一樣的,那這部分數據就可以公用,就是享元模式中的內在數據,定義享元類如下:
function IphoneFlyweight(model, screen, memory) { this.model = model; this.screen = screen; this.memory = memory; }
我們定義了iphone的享元類,其中包含型號,螢幕和記憶體三個數據。我們還需要一個享元工廠來維護這些資料:
var flyweightFactory = (function () { var iphones = {}; return { get: function (model, screen, memory) { var key = model + screen + memory; if (!iphones[key]) { iphones[key] = new IphoneFlyweight(model, screen, memory); } return iphones[key]; } }; })();
在這個工廠中,我們定義了一個字典來保存享元對象,提供一個方法根據參數來取得享元對象,如果字典中有則直接返回,沒有則建立一個返回。
接著我們建立一個客戶端類,這個客戶端類別就是修改自iphone類:
function Iphone(model, screen, memory, SN) { this.flyweight = flyweightFactory.get(model, screen, memory); this.SN = SN; }
然後我們依舊像之間那樣產生多個iphone
var phones = []; for (var i = 0; i < 1000000; i++) { var memory = i % 2 == 0 ? 16 : 32; phones.push(new Iphone("iphone6s", 5.0, memory, i)); } console.log(phones);
這裡的關鍵就在於Iphone建構子裡面的this.flyweight = flyweightFactory.get(model, screen, memory)。這句程式碼透過享元工廠去獲取享元數據,而在享元工廠裡面,如果已經存在相同數據的對象則會直接返回對象,多個iphone對象共享這部分相同的數據,所以原本類似的數據已經大大減少,減少的記憶體的佔用。
享元模式在DOM中的應用
享元模式的一個典型應用就是DOM事件操作,DOM事件機制分成事件冒泡和事件擷取。我們簡單介紹一下這兩者:
事件冒泡:綁定的事件從最裡層的元素開始觸發,然後冒泡到最外層
事件捕獲:綁定的事件從最外層的元素開始觸發,然後傳到最裡層
假設我們HTML中有一個選單清單
<ul class="menu"> <li class="item">选项1</li> <li class="item">选项2</li> <li class="item">选项3</li> <li class="item">选项4</li> <li class="item">选项5</li> <li class="item">选项6</li> </ul>
點擊選單項,進行對應的動作,我們透過jQuery來綁定事件,一般會這麼做:
$(".item").on("click", function () { console.log($(this).text()); })
#給每個清單項目綁定事件,點選輸出對應的文字。這樣看暫時沒有什麼問題,但是如果是一個很長的列表,尤其是在行動端特別長的列表時,就會有效能問題,因為每個項目都綁定了事件,都佔用了記憶體。但這些事件處理程序其實都是很類似的,我們就要對其進行最佳化。
$(".menu").on("click", ".item", function () { console.log($(this).text()); })
透過這種方式進行事件綁定,可以減少事件處理程序的數量,這種方式叫做事件委託,也是運用了享元模式的原理。事件處理程序是公用的內在部分,每個選單項目各自的文字就是外在部分。我們簡單說下事件委託的原理:點選選單項,事件會從li元素冒泡到ul元素,我們綁定事件到ul上,實際上就綁定了一個事件,然後透過事件參數event裡面的target來判斷點擊的具體是哪一個元素,例如低階第一個li元素,event.target就是li,這樣就能拿到具體的點擊元素了,就可以根據不同元素進行不同的處理。
總結
#享元模式是一種最佳化程式效能的手段,透過共享公用資料來減少物件數量以達到最佳化程式的手段。享元模式適用於擁有大量類似物件並且對效能有要求的場景。因為享元模式需要分離內部和外部數據,增加了程式的邏輯複雜性,建議對效能有要求的時候才使用享元模式。
享元模式之利:
可以把網頁的資源符合降低幾個數量級。即使享元模式的應用無法將實例的數量削減到一個,你仍能夠從中獲益不少。
這種節省不需要大量修改原有程式碼。在創建了管理器、工廠和享元之後,就需要對程式碼進行的修改只不過是從直接實例化目標類別改為呼叫管理器物件的某個方法。
享元模式之弊:
如果把它用在不必要的地方,其結果反而有損程式碼的運作效率。這種模式在優化程式碼的同時,也提高了其複雜程度,這會對調試和維護造成困難。
它之所以會妨礙調試,是因為現在可能出錯的地方變成了三個:管理器、工廠和享元。
這種最佳化也會使維護變得更加困難。現在你面對的不是由封裝著資料的物件構成的清晰架構,而是一堆又碎又亂的東西。其中的數據至少分兩處保存。最好註釋標明內在資料和外在資料。
只有在必要的時候才應該進行這種最佳化。必須在運作效率和可維護性之間進行權衡。如果拿不準是否需要使用享元模式,那麼你很可能並不需要它。享元模式適合的是系統資源已經用得差不多而且明顯需要進行某種優化這樣一類場合。
這個模式對Javascript程式設計師特別有用,因為它可以用來減少網頁上所要使用的DOM元素的數量,要知道這些元素需要耗費許多記憶體。結合使用這種模式與組合模式等組織型可以開發出功能豐富的複雜Web應用系統,它們可以平穩的運作在任何現代Javascript環境中。
享元模式的適用場合:
網頁中必須使用了大量資源密集型物件。如果只會用到少許這類對象,這種優化並不划算。
物件中所保存的資料至少有一部分能被轉換為外在資料。此外,將這些資料儲存在物件外部所佔用的資源應該相對較少,否則這種做法對於效能的提示實際上毫無意義。那種大量包含基礎性程式碼和HTML內容的物件可能比較適合這種最佳化。
將外在資料分開後,獨一無二的物件的數目相對較少。
上面是我整理給大家的,希望今後對大家有幫助。
相關文章:
#對javascript Function函數深入理解與實戰(附上程式碼)
#
以上是詳細解讀在JavaScript設計模式程式設計中的享元模式的詳細內容。更多資訊請關注PHP中文網其他相關文章!