在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ HTML/ 設(shè)計(jì)模式之享元模式
代碼復(fù)用模式(避免篇)
S.O.L.I.D 五大原則之接口隔離原則 ISP
設(shè)計(jì)模式之狀態(tài)模式
JavaScript 核心(晉級(jí)高手必讀篇)
設(shè)計(jì)模式之建造者模式
JavaScript 與 DOM(上)——也適用于新手
設(shè)計(jì)模式之中介者模式
設(shè)計(jì)模式之裝飾者模式
設(shè)計(jì)模式之模板方法
設(shè)計(jì)模式之外觀模式
強(qiáng)大的原型和原型鏈
設(shè)計(jì)模式之構(gòu)造函數(shù)模式
揭秘命名函數(shù)表達(dá)式
深入理解J avaScript 系列(結(jié)局篇)
執(zhí)行上下文(Execution Contexts)
函數(shù)(Functions)
《你真懂 JavaScript 嗎?》答案詳解
設(shè)計(jì)模式之適配器模式
設(shè)計(jì)模式之組合模式
設(shè)計(jì)模式之命令模式
S.O.L.I.D 五大原則之單一職責(zé) SRP
編寫高質(zhì)量 JavaScript 代碼的基本要點(diǎn)
求值策略
閉包(Closures)
對象創(chuàng)建模式(上篇)
This? Yes,this!
設(shè)計(jì)模式之代理模式
變量對象(Variable Object)
S.O.L.I.D 五大原則之里氏替換原則 LSP
面向?qū)ο缶幊讨话憷碚?/span>
設(shè)計(jì)模式之單例模式
Function 模式(上篇)
S.O.L.I.D 五大原則之依賴倒置原則 DIP
設(shè)計(jì)模式之迭代器模式
立即調(diào)用的函數(shù)表達(dá)式
設(shè)計(jì)模式之享元模式
設(shè)計(jì)模式之原型模式
根本沒有“JSON 對象”這回事!
JavaScript 與 DOM(下)
面向?qū)ο缶幊讨?ECMAScript 實(shí)現(xiàn)
全面解析 Module 模式
對象創(chuàng)建模式(下篇)
設(shè)計(jì)模式之職責(zé)鏈模式
S.O.L.I.D 五大原則之開閉原則 OCP
設(shè)計(jì)模式之橋接模式
設(shè)計(jì)模式之策略模式
設(shè)計(jì)模式之觀察者模式
代碼復(fù)用模式(推薦篇)
作用域鏈(Scope Chain)
Function 模式(下篇)
設(shè)計(jì)模式之工廠模式

設(shè)計(jì)模式之享元模式

介紹

享元模式(Flyweight),運(yùn)行共享技術(shù)有效地支持大量細(xì)粒度的對象,避免大量擁有相同內(nèi)容的小類的開銷(如耗費(fèi)內(nèi)存),使大家共享一個(gè)類(元類)。

享元模式可以避免大量非常相似類的開銷,在程序設(shè)計(jì)中,有時(shí)需要生產(chǎn)大量細(xì)粒度的類實(shí)例來表示數(shù)據(jù),如果能發(fā)現(xiàn)這些實(shí)例除了幾個(gè)參數(shù)以外,開銷基本相同的 話,就可以大幅度較少需要實(shí)例化的類的數(shù)量。如果能把那些參數(shù)移動(dòng)到類實(shí)例的外面,在方法調(diào)用的時(shí)候?qū)⑺麄儌鬟f進(jìn)來,就可以通過共享大幅度第減少單個(gè)實(shí)例 的數(shù)目。

那么如果在 JavaScript 中應(yīng)用享元模式呢?有兩種方式,第一種是應(yīng)用在數(shù)據(jù)層上,主要是應(yīng)用在內(nèi)存里大量相似的對象上;第二種是應(yīng)用在 DOM 層上,享元可以用在中央事件管理器上用來避免給父容器里的每個(gè)子元素都附加事件句柄。

享元與數(shù)據(jù)層

Flyweight 中有兩個(gè)重要概念--內(nèi)部狀態(tài) intrinsic 和外部狀態(tài) extrinsic 之分,內(nèi)部狀態(tài)就是在對象里通過內(nèi)部方法管理,而外部信息可以在通過外部刪除或者保存。

說白點(diǎn),就是先捏一個(gè)的原始模型,然后隨著不同場合和環(huán)境,再產(chǎn)生各具特征的具體模型,很顯然,在這里需要產(chǎn)生不同的新對象,所以 Flyweight 模式中常出現(xiàn) Factory 模式,F(xiàn)lyweight 的內(nèi)部狀態(tài)是用來共享的,F(xiàn)lyweight factory 負(fù)責(zé)維護(hù)一個(gè) Flyweight pool(模式池)來存放內(nèi)部狀態(tài)的對象。

使用享元模式

讓我們來演示一下如果通過一個(gè)類庫讓系統(tǒng)來管理所有的書籍,每個(gè)書籍的元數(shù)據(jù)暫定為如下內(nèi)容:

ID
Title
Author
Genre
Page count
Publisher ID
ISBN

我們還需要定義每本書被借出去的時(shí)間和借書人,以及退書日期和是否可用狀態(tài):

checkoutDate
checkoutMember
dueReturnDate
availability

因?yàn)?book 對象設(shè)置成如下代碼,注意該代碼還未被優(yōu)化:

var Book = function( id, title, author, genre, pageCount,publisherID, ISBN, checkoutDate, checkoutMember, dueReturnDate,availability ){
   this.id = id;
   this.title = title;
   this.author = author;
   this.genre = genre;
   this.pageCount = pageCount;
   this.publisherID = publisherID;
   this.ISBN = ISBN;
   this.checkoutDate = checkoutDate;
   this.checkoutMember = checkoutMember;
   this.dueReturnDate = dueReturnDate;
   this.availability = availability;
};
Book.prototype = {
   getTitle:function(){
       return this.title;
   },
   getAuthor: function(){
       return this.author;
   },
   getISBN: function(){
       return this.ISBN;
   },
/*其它get方法在這里就不顯示了*/
// 更新借出狀態(tài)
updateCheckoutStatus: function(bookID, newStatus, checkoutDate,checkoutMember, newReturnDate){
   this.id  = bookID;
   this.availability = newStatus;
   this.checkoutDate = checkoutDate;
   this.checkoutMember = checkoutMember;
   this.dueReturnDate = newReturnDate;
},
//續(xù)借
extendCheckoutPeriod: function(bookID, newReturnDate){
    this.id =  bookID;
    this.dueReturnDate = newReturnDate;
},
//是否到期
isPastDue: function(bookID){
   var currentDate = new Date();
   return currentDate.getTime() > Date.parse(this.dueReturnDate);
 }
};

程序剛開始可能沒問題,但是隨著時(shí)間的增加,圖書可能大批量增加,并且每種圖書都有不同的版本和數(shù)量,你將會(huì)發(fā)現(xiàn)系統(tǒng)變得越來越慢。幾千個(gè) book 對象在內(nèi)存里可想而知,我們需要用享元模式來優(yōu)化。

我們可以將數(shù)據(jù)分成內(nèi)部和外部兩種數(shù)據(jù),和 book 對象相關(guān)的數(shù)據(jù)(title,author 等)可以歸結(jié)為內(nèi)部屬性,而(checkoutMember,dueReturnDate 等)可以歸結(jié)為外部屬性。這樣,如下代碼就可以在同一本書里共享同一個(gè)對象了,因?yàn)椴还苷l借的書,只要書是同一本書,基本信息是一樣的:

/*享元模式優(yōu)化代碼*/
var Book = function(title, author, genre, pageCount, publisherID, ISBN){
   this.title = title;
   this.author = author;
   this.genre = genre;
   this.pageCount = pageCount;
   this.publisherID = publisherID;
   this.ISBN = ISBN;
};

定義基本工廠

讓我們來定義一個(gè)基本工廠,用來檢查之前是否創(chuàng)建該 book 的對象,如果有就返回,沒有就重新創(chuàng)建并存儲(chǔ)以便后面可以繼續(xù)訪問,這確保我們?yōu)槊恳环N書只創(chuàng)建一個(gè)對象:

/* Book工廠 單例 */
var BookFactory = (function(){
   var existingBooks = {};
   return{
       createBook: function(title, author, genre,pageCount,publisherID,ISBN){
       /*查找之前是否創(chuàng)建*/
           var existingBook = existingBooks[ISBN];
           if(existingBook){
                   return existingBook;
               }else{
               /* 如果沒有,就創(chuàng)建一個(gè),然后保存*/
               var book = new Book(title, author, genre,pageCount,publisherID,ISBN);
               existingBooks[ISBN] =  book;
               return book;
           }
       }
   }
});

管理外部狀態(tài)

外部狀態(tài),相對就簡單了,除了我們封裝好的 book,其它都需要在這里管理:

/*BookRecordManager 借書管理類 單例*/
var BookRecordManager = (function(){
   var bookRecordDatabase = {};
   return{
       /*添加借書記錄*/
       addBookRecord: function(id, title, author, genre,pageCount,publisherID,ISBN, checkoutDate, checkoutMember, dueReturnDate, availability){
           var book = bookFactory.createBook(title, author, genre,pageCount,publisherID,ISBN);
            bookRecordDatabase[id] ={
               checkoutMember: checkoutMember,
               checkoutDate: checkoutDate,
               dueReturnDate: dueReturnDate,
               availability: availability,
               book: book;
           };
       },
    updateCheckoutStatus: function(bookID, newStatus, checkoutDate, checkoutMember,     newReturnDate){
        var record = bookRecordDatabase[bookID];
        record.availability = newStatus;
        record.checkoutDate = checkoutDate;
        record.checkoutMember = checkoutMember;
        record.dueReturnDate = newReturnDate;
   },
   extendCheckoutPeriod: function(bookID, newReturnDate){
       bookRecordDatabase[bookID].dueReturnDate = newReturnDate;
   },
   isPastDue: function(bookID){
       var currentDate = new Date();
       return currentDate.getTime() > Date.parse(bookRecordDatabase[bookID].dueReturnDate);
   }
 };
});

通過這種方式,我們做到了將同一種圖書的相同信息保存在一個(gè) bookmanager 對象里,而且只保存一份;相比之前的代碼,就可以發(fā)現(xiàn)節(jié)約了很多內(nèi)存。

享元模式與 DOM

關(guān)于 DOM 的事件冒泡,在這里就不多說了,相信大家都已經(jīng)知道了,我們舉兩個(gè)例子。

例 1:事件集中管理

舉例來說,如果我們又很多相似類型的元素或者結(jié)構(gòu)(比如菜單,或者 ul 里的多個(gè) li)都需要監(jiān)控他的 click 事件的話,那就需要多每個(gè)元素進(jìn)行事件綁定,如果元素有非常非常多,那性能就可想而知了,而結(jié)合冒泡的知識(shí),任何一個(gè)子元素有事件觸發(fā)的話,那觸發(fā)以后事件將冒泡到上一級(jí)元素,所以利用這個(gè)特性,我們可以使用享元模式,我們可以對這些相似元素的父級(jí)元素進(jìn)行事件監(jiān)控,然后再判斷里面哪個(gè)子元素有事件觸發(fā)了,再進(jìn)行進(jìn)一步的操作。

在這里我們結(jié)合一下 jQuery 的 bind/unbind 方法來舉例。

HTML

<div id="container">
   <div class="toggle" href="#">更多信息 (地址)
       <span class="info">
          這里是更多信息
       </span></div>
   <div class="toggle" href="#">更多信息 (地圖)
       <span class="info">
          <iframe src="http://www.map-generator.net/extmap.php?name=London&amp;address=london%2C%20england&amp;width=500...gt;"</iframe>
       </span>
   </div>
</div>

JavaScript

stateManager = {
   fly: function(){
       var self =  this;
       $('#container').unbind().bind("click", function(e){
           var target = $(e.originalTarget || e.srcElement);
           // 判斷是哪一個(gè)子元素
           if(target.is("div.toggle")){
               self.handleClick(target);
           }
       });
   },
   handleClick: function(elem){
       elem.find('span').toggle('slow');
   }
});

例 2:應(yīng)用享元模式提升性能

另外一個(gè)例子,依然和 jQuery 有關(guān),一般我們在事件的回調(diào)函數(shù)里使用元素對象是會(huì)后,經(jīng)常會(huì)用到$(this)這種形式,其實(shí)它重復(fù)創(chuàng)建了新對象,因?yàn)楸旧砘卣{(diào)函數(shù)里的 this 已經(jīng)是 DOM 元素自身了,我們必要必要使用如下這樣的代碼:

$('div').bind('click', function(){
 console.log('You clicked: ' + $(this).attr('id'));
});
// 上面的代碼,要避免使用,避免再次對DOM元素進(jìn)行生成jQuery對象,因?yàn)檫@里可以直接使用DOM元素自身了。
$('div').bind('click', function(){
 console.log('You clicked: ' + this.id);
});

其實(shí),如果非要用 $(this)這樣的形式,我們也可以實(shí)現(xiàn)自己版本的單實(shí)例模式,比如我們來實(shí)現(xiàn)一個(gè) jQuery.signle(this)這樣的函數(shù)以便返回DOM元素自身:

jQuery.single = (function(o){
   var collection = jQuery([1]);
   return function(element) {
       // 將元素放到集合里
       collection[0] = element;
        // 返回集合
       return collection;
   };
 });

使用方法:

$('div').bind('click', function(){
   var html = jQuery.single(this).next().html();
   console.log(html);
 });

這樣,就是原樣返回 DOM 元素自身了,而且不進(jìn)行 jQuery 對象的創(chuàng)建。

總結(jié)

Flyweight 模式是一個(gè)提高程序效率和性能的模式,會(huì)大大加快程序的運(yùn)行速度.應(yīng)用場合很多:比如你要從一個(gè)數(shù)據(jù)庫中讀取一系列字符串,這些字符串中有許多是重復(fù)的,那么我們可以將這些字符串儲(chǔ)存在 Flyweight 池(pool)中。

如果一個(gè)應(yīng)用程序使用了大量的對象,而這些大量的對象造成了很大的存儲(chǔ)開心時(shí)就應(yīng)該考慮使用享元模式;還有就是對象的大多數(shù)狀態(tài)可以外部狀態(tài),如果刪除對象的外部狀態(tài),那么就可以用相對較少的共享對象取代很多組對象,此時(shí)可以考慮使用享元模式。