總結(jié)JavaScript設(shè)計(jì)模式編程中的享元模式使用
來(lái)源:易賢網(wǎng) 閱讀:674 次 日期:2016-06-25 14:14:50
溫馨提示:易賢網(wǎng)小編為您整理了“總結(jié)JavaScript設(shè)計(jì)模式編程中的享元模式使用”,方便廣大網(wǎng)友查閱!

享元模式不同于一般的設(shè)計(jì)模式,它主要用來(lái)優(yōu)化程序的性能,它最適合解決大量類(lèi)似的對(duì)象而產(chǎn)生的性能問(wèn)題。享元模式通過(guò)分析應(yīng)用程序的對(duì)象,將其解析為內(nèi)在數(shù)據(jù)和外在數(shù)據(jù),減少對(duì)象的數(shù)量,從而提高應(yīng)用程序的性能。

基本知識(shí)

享元模式通過(guò)共享大量的細(xì)粒度的對(duì)象,減少對(duì)象的數(shù)量,從而減少對(duì)象的內(nèi)存,提高應(yīng)用程序的性能。其基本思想就是分解現(xiàn)有類(lèi)似對(duì)象的組成,將其展開(kāi)為可以共享的內(nèi)在數(shù)據(jù)和不可共享的外在數(shù)據(jù),我們稱(chēng)內(nèi)在數(shù)據(jù)的對(duì)象為享元對(duì)象。通常還需要一個(gè)工廠類(lèi)來(lái)維護(hù)內(nèi)在數(shù)據(jù)。

在JS中,享元模式主要有下面幾個(gè)角色組成:

(1)客戶(hù)端:用來(lái)調(diào)用享元工廠來(lái)獲取內(nèi)在數(shù)據(jù)的類(lèi),通常是應(yīng)用程序所需的對(duì)象,

(2)享元工廠:用來(lái)維護(hù)享元數(shù)據(jù)的類(lèi)

(3)享元類(lèi):保持內(nèi)在數(shù)據(jù)的類(lèi)

享元模式的實(shí)現(xiàn)和應(yīng)用

一般實(shí)現(xiàn)

我們舉個(gè)例子進(jìn)行說(shuō)明:蘋(píng)果公司批量生產(chǎn)iphone,iphone的大部分?jǐn)?shù)據(jù)比如型號(hào),屏幕都是一樣,少數(shù)部分?jǐn)?shù)據(jù)比如內(nèi)存有分16G,32G等。未使用享元模式前,我們寫(xiě)代碼如下:

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));

}

這段代碼中,創(chuàng)建了一百萬(wàn)個(gè)iphone,每個(gè)iphone都獨(dú)立申請(qǐng)一個(gè)內(nèi)存。但是我們仔細(xì)觀察可以看到,大部分iphone都是類(lèi)似的,只是內(nèi)存和序列號(hào)不一樣,如果是一個(gè)對(duì)性能要求比較高的程序,我們就要考慮去優(yōu)化它。

大量相似對(duì)象的程序,我們就可以考慮用享元模式去優(yōu)化它,我們分析出大部分的iphone的型號(hào),屏幕,內(nèi)存都是一樣的,那這部分?jǐn)?shù)據(jù)就可以公用,就是享元模式中的內(nèi)在數(shù)據(jù),定義享元類(lèi)如下:

function IphoneFlyweight(model, screen, memory) {

  this.model = model;

  this.screen = screen;

  this.memory = memory;

}

我們定義了iphone的享元類(lèi),其中包含型號(hào),屏幕和內(nèi)存三個(gè)數(shù)據(jù)。我們還需要一個(gè)享元工廠來(lái)維護(hù)這些數(shù)據(jù):

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];

    }

  };

})();

在這個(gè)工廠中,我們定義了一個(gè)字典來(lái)保存享元對(duì)象,提供一個(gè)方法根據(jù)參數(shù)來(lái)獲取享元對(duì)象,如果字典中有則直接返回,沒(méi)有則創(chuàng)建一個(gè)返回。

接著我們創(chuàng)建一個(gè)客戶(hù)端類(lèi),這個(gè)客戶(hù)端類(lèi)就是修改自iphone類(lèi):

function Iphone(model, screen, memory, SN) {

  this.flyweight = flyweightFactory.get(model, screen, memory);

  this.SN = SN;

}

然后我們依舊像之間那樣生成多個(gè)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);

這里的關(guān)鍵就在于Iphone構(gòu)造函數(shù)里面的this.flyweight = flyweightFactory.get(model, screen, memory)。這句代碼通過(guò)享元工廠去獲取享元數(shù)據(jù),而在享元工廠里面,如果已經(jīng)存在相同數(shù)據(jù)的對(duì)象則會(huì)直接返回對(duì)象,多個(gè)iphone對(duì)象共享這部分相同的數(shù)據(jù),所以原本類(lèi)似的數(shù)據(jù)已經(jīng)大大減少,減少的內(nèi)存的占用。

享元模式在DOM中的應(yīng)用

享元模式的一個(gè)典型應(yīng)用就是DOM事件操作,DOM事件機(jī)制分成事件冒泡和事件捕獲。我們簡(jiǎn)單介紹一下這兩者:

事件冒泡:綁定的事件從最里層的元素開(kāi)始觸發(fā),然后冒泡到最外層

事件捕獲:綁定的事件從最外層的元素開(kāi)始觸發(fā),然后傳到最里層

假設(shè)我們HTML中有一個(gè)菜單列表

<ul class="menu">

  <li class="item">選項(xiàng)1</li>

  <li class="item">選項(xiàng)2</li>

  <li class="item">選項(xiàng)3</li>

  <li class="item">選項(xiàng)4</li>

  <li class="item">選項(xiàng)5</li>

  <li class="item">選項(xiàng)6</li>

</ul>

點(diǎn)擊菜單項(xiàng),進(jìn)行相應(yīng)的操作,我們通過(guò)jQuery來(lái)綁定事件,一般會(huì)這么做:

$(".item").on("click", function () {

  console.log($(this).text());

})

給每個(gè)列表項(xiàng)綁定事件,點(diǎn)擊輸出相應(yīng)的文本。這樣看暫時(shí)沒(méi)有什么問(wèn)題,但是如果是一個(gè)很長(zhǎng)的列表,尤其是在移動(dòng)端特別長(zhǎng)的列表時(shí),就會(huì)有性能問(wèn)題,因?yàn)槊總€(gè)項(xiàng)都綁定了事件,都占用了內(nèi)存。但是這些事件處理程序其實(shí)都是很類(lèi)似的,我們就要對(duì)其優(yōu)化。

$(".menu").on("click", ".item", function () {

  console.log($(this).text());

})

通過(guò)這種方式進(jìn)行事件綁定,可以減少事件處理程序的數(shù)量,這種方式叫做事件委托,也是運(yùn)用了享元模式的原理。事件處理程序是公用的內(nèi)在部分,每個(gè)菜單項(xiàng)各自的文本就是外在部分。我們簡(jiǎn)單說(shuō)下事件委托的原理:點(diǎn)擊菜單項(xiàng),事件會(huì)從li元素冒泡到ul元素,我們綁定事件到ul上,實(shí)際上就綁定了一個(gè)事件,然后通過(guò)事件參數(shù)event里面的target來(lái)判斷點(diǎn)擊的具體是哪一個(gè)元素,比如低級(jí)第一個(gè)li元素,event.target就是li,這樣就能拿到具體的點(diǎn)擊元素了,就可以根據(jù)不同元素進(jìn)行不同的處理。

總結(jié)

享元模式是一種優(yōu)化程序性能的手段,通過(guò)共享公用數(shù)據(jù)來(lái)減少對(duì)象數(shù)量以達(dá)到優(yōu)化程序的手段。享元模式適用于擁有大量類(lèi)似對(duì)象并且對(duì)性能有要求的場(chǎng)景。因?yàn)橄碓J叫枰蛛x內(nèi)部和外部數(shù)據(jù),增加了程序的邏輯復(fù)雜性,建議對(duì)性能有要求的時(shí)候才使用享元模式。

享元模式之利:

可以把網(wǎng)頁(yè)的資源符合降低幾個(gè)數(shù)量級(jí)。即使享元模式的應(yīng)用無(wú)法將實(shí)例的個(gè)數(shù)削減到一個(gè),你仍能夠從中獲益不少。

這種節(jié)省不需要大量修改原有代碼。在創(chuàng)建了管理器、工廠和享元之后,就需要對(duì)代碼進(jìn)行的修改只不過(guò)是從直接實(shí)例化目標(biāo)類(lèi)改為調(diào)用管理器對(duì)象的某個(gè)方法。

享元模式之弊:

如果把它用在不必要的地方,其結(jié)果反而有損代碼的運(yùn)行效率。這種模式在優(yōu)化代碼的同時(shí),也提高了其復(fù)雜程度,這會(huì)給調(diào)試和維護(hù)造成困難。

它之所以會(huì)妨礙調(diào)試,是因?yàn)楝F(xiàn)在可能出錯(cuò)的地方變成了三個(gè):管理器、工廠和享元。

這種優(yōu)化也會(huì)使維護(hù)變得更加困難。現(xiàn)在你面對(duì)的不是由封裝著數(shù)據(jù)的對(duì)象構(gòu)成的清晰架構(gòu),而是一堆又碎又亂的東西。其中的數(shù)據(jù)至少分兩處保存。最好注釋標(biāo)明內(nèi)在數(shù)據(jù)和外在數(shù)據(jù)。

只有在必要的時(shí)候才應(yīng)該進(jìn)行這種優(yōu)化。必須在運(yùn)行效率和可維護(hù)性之間進(jìn)行權(quán)衡。如果拿不準(zhǔn)是否需要使用享元模式,那么你很可能并不需要它。享元模式適合的是系統(tǒng)資源已經(jīng)用得差不多而且明顯需要進(jìn)行某種優(yōu)化這樣一類(lèi)場(chǎng)合。

這種模式對(duì)Javascript程序員特別有用,因?yàn)樗梢杂脕?lái)減少網(wǎng)頁(yè)上所要使用的DOM元素的數(shù)量,要知道這些元素需要耗費(fèi)許多內(nèi)存。結(jié)合使用這種模式與組合模式等組織型可以開(kāi)發(fā)出功能豐富的復(fù)雜Web應(yīng)用系統(tǒng),它們可以平穩(wěn)的運(yùn)行在任何現(xiàn)代Javascript環(huán)境中。

享元模式的適用場(chǎng)合:

網(wǎng)頁(yè)中必須使用了大量資源密集型對(duì)象。如果只會(huì)用到少許這類(lèi)對(duì)象,這種優(yōu)化并不劃算。

對(duì)象中所保存的數(shù)據(jù)至少有一部分能被轉(zhuǎn)化為外在數(shù)據(jù)。此外,將這些數(shù)據(jù)存儲(chǔ)在對(duì)象外部所占用的資源應(yīng)該相對(duì)較少,否則這種做法對(duì)于性能的提示實(shí)際上毫無(wú)意義。那種大量包含基礎(chǔ)性代碼和HTML內(nèi)容的對(duì)象可能比較適合這種優(yōu)化。

將外在數(shù)據(jù)分離出去后,獨(dú)一無(wú)二的對(duì)象的數(shù)目相對(duì)較少。

更多信息請(qǐng)查看網(wǎng)絡(luò)編程
由于各方面情況的不斷調(diào)整與變化,易賢網(wǎng)提供的所有考試信息和咨詢(xún)回復(fù)僅供參考,敬請(qǐng)考生以權(quán)威部門(mén)公布的正式信息和咨詢(xún)?yōu)闇?zhǔn)!

2025國(guó)考·省考課程試聽(tīng)報(bào)名

  • 報(bào)班類(lèi)型
  • 姓名
  • 手機(jī)號(hào)
  • 驗(yàn)證碼
關(guān)于我們 | 聯(lián)系我們 | 人才招聘 | 網(wǎng)站聲明 | 網(wǎng)站幫助 | 非正式的簡(jiǎn)要咨詢(xún) | 簡(jiǎn)要咨詢(xún)須知 | 新媒體/短視頻平臺(tái) | 手機(jī)站點(diǎn) | 投訴建議
工業(yè)和信息化部備案號(hào):滇ICP備2023014141號(hào)-1 云南省教育廳備案號(hào):云教ICP備0901021 滇公網(wǎng)安備53010202001879號(hào) 人力資源服務(wù)許可證:(云)人服證字(2023)第0102001523號(hào)
云南網(wǎng)警備案專(zhuān)用圖標(biāo)
聯(lián)系電話:0871-65099533/13759567129 獲取招聘考試信息及咨詢(xún)關(guān)注公眾號(hào):hfpxwx
咨詢(xún)QQ:1093837350(9:00—18:00)版權(quán)所有:易賢網(wǎng)
云南網(wǎng)警報(bào)警專(zhuān)用圖標(biāo)