輕松掌握JavaScript享元模式
在JavaScript中,瀏覽器特別是移動端的瀏覽器分配的內(nèi)存很有限,如何節(jié)省內(nèi)存就成了一件非常有意義的事情。節(jié)省內(nèi)存的一個有效方法是減少對象的數(shù)量。
享元模式(Flyweight),運行共享技術(shù)有效地支持大量細粒度的對象,避免大量擁有相同內(nèi)容的小類的開銷(如耗費內(nèi)存),使大家共享一個類(元類)。
享元模式可以避免大量非常相似類的開銷,在程序設(shè)計中,有時需要生產(chǎn)大量細粒度的類實例來表示數(shù)據(jù),如果能發(fā)現(xiàn)這些實例除了幾個參數(shù)以外,開銷基本相同的話,就可以大幅度較少需要實例化的類的數(shù)量。如果能把那些參數(shù)移動到類實例的外面,在方法調(diào)用的時候?qū)⑺麄儌鬟f進來,就可以通過共享大幅度第減少單個實例 的數(shù)目。
在JavaScript中應(yīng)用享元模式有兩種方式,第一種是應(yīng)用在數(shù)據(jù)層上,主要是應(yīng)用在內(nèi)存里大量相似的對象上;第二種是應(yīng)用在DOM層上,享元可以用在中央事件管理器上用來避免給父容器里的每個子元素都附加事件句柄
Flyweight中有兩個重要概念--內(nèi)部狀態(tài)intrinsic和外部狀態(tài)extrinsic之分,內(nèi)部狀態(tài)就是在對象里通過內(nèi)部方法管理,而外部信息可以在通過外部刪除或者保存。
說白點,就是先捏一個的原始模型,然后隨著不同場合和環(huán)境,再產(chǎn)生各具特征的具體模型,很顯然,在這里需要產(chǎn)生不同的新對象,所以Flyweight模式中常出現(xiàn)Factory模式,F(xiàn)lyweight的內(nèi)部狀態(tài)是用來共享的,F(xiàn)lyweight factory負責維護一個Flyweight pool(模式池)來存放內(nèi)部狀態(tài)的對象。
我們可以將內(nèi)部狀態(tài)相同的所有對象替換為同一個共享對象,而要創(chuàng)建這樣一個共享對象就需要用到單例工廠方法,而不是普通的構(gòu)造函數(shù),這樣做可以跟蹤到已經(jīng)實例化的各個對象,從而僅當所需對象的內(nèi)部狀態(tài)不同于已有對象時才創(chuàng)建一個新對象。對象的外在狀態(tài)被保存在一個管理器對象中。在調(diào)用對象的方法時,管理器會把這些外在狀態(tài)作為參數(shù)傳入。
把一個對象的數(shù)據(jù)保存在兩個不同的對象中(共享對象、管理器對象)
1.共享對象(享元對象)
2.單例工廠方法(創(chuàng)建共享對象)
3.管理器對象(管理外部狀態(tài))
比如圖書館中的一本書可以用一個對象來表示,他有很多屬性
var Book = function( id, title, author, genre, pageCount,publisherID, ISBN, checkoutDate, checkoutMember, dueReturnDate,availability ){ ...//初始化代碼 } Book.prototype = { getTitle:function(){ return this.title; }, ... // 更新借出狀態(tài)方法 updateCheckoutStatus:function(bookID, newStatus, checkoutDate,checkoutMember, newReturnDate){...}, //續(xù)借 extendCheckoutPeriod: function(bookID, newReturnDate){...}, //是否到期 isPastDue: function(bookID){...} }
程序剛開始可能沒問題,但是隨著時間的增加,圖書可能大批量增加,并且每種圖書都有不同的版本和數(shù)量,你將會發(fā)現(xiàn)系統(tǒng)變得越來越慢。幾千個book對象在內(nèi)存里可想而知,我們需要用享元模式來優(yōu)化。
我們可以將數(shù)據(jù)分成內(nèi)部和外部兩種數(shù)據(jù),同一本書中,和book對象相關(guān)的數(shù)據(jù)(title,author等)可以歸結(jié)為內(nèi)部屬性,而(checkoutMember,dueReturnDate等)可以歸結(jié)為外部屬性。這樣,如下代碼就可以在同一本書里共享同一個對象了,因為不管誰借的書,只要書是同一本書,基本信息是一樣的:
//共享對象 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; };
讓我們來定義一個基本工廠,用來檢查之前是否創(chuàng)建該book的對象,如果有就返回,沒有就重新創(chuàng)建并存儲以便后面可以繼續(xù)訪問,這確保我們?yōu)槊恳环N書只創(chuàng)建一個對象:
/* 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)建一個,然后保存*/ var book = new Book(title, author, genre,pageCount,publisherID,ISBN); existingBooks[ISBN] = book; return book; } } } });
外部狀態(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); } }; });
通過這種方式,我們做到了將同一種圖書的相同信息保存在一個bookmanager對象里,而且只保存一份;相比之前的代碼,就可以發(fā)現(xiàn)節(jié)約了很多內(nèi)存。
對象池
對象池是另外一種性能優(yōu)化方案,和享元模式有一些相似之處,但沒有分離內(nèi)部狀態(tài)和外部狀態(tài)這個過程。
通用對象池實現(xiàn):
var objectPoolFactory = function (createObjFn) { var objectPool = []; //對象池 return { create: function () { //取出 var obj = objectPool.length === 0 ? createObjFn.apply(this,arguments) : objectPool.shift(); return obj; }, recover: function (obj) { //收回 objectPool.push(obj); } } };
現(xiàn)在利用objectPoolFactory來創(chuàng)建一個裝載一些iframe的對象池:
var iframeFactory = objectPoolFactory(function () { var iframe = document.createElement('iframe'); document.body.appendChild(iframe); iframe.onload = function () { iframe.onload = null; //防止iframe重復(fù)加載的bug iframeFactory.recover(iframe); //iframe加載完成后往對象池填回節(jié)點(收回) }; return iframe; }); //調(diào)用 var iframe1 = iframeFactory.create(); iframe1.src = 'http://www.qq.com';
參考文獻: 《JavaScript模式》 《JavaScript設(shè)計模式與開發(fā)實踐》
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- javascript設(shè)計模式之享元模式
- javascript設(shè)計模式 – 享元模式原理與用法實例分析
- javascript 設(shè)計模式之享元模式原理與應(yīng)用詳解
- JavaScript享元模式原理與用法實例詳解
- JavaScript設(shè)計模式之享元模式實例詳解
- JavaScript使用享元模式實現(xiàn)文件上傳優(yōu)化操作示例
- js設(shè)計模式之結(jié)構(gòu)型享元模式詳解
- 學(xué)習(xí)JavaScript設(shè)計模式之享元模式
- JS實現(xiàn)簡單的圖書館享元模式實例
- JavaScript設(shè)計模式之性能優(yōu)化模式享元模式
相關(guān)文章
JavaScript實現(xiàn)簡易飛機大戰(zhàn)
這篇文章主要為大家詳細介紹了JavaScript實現(xiàn)簡易飛機大戰(zhàn),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-05-05基于layui數(shù)據(jù)表格以及傳數(shù)據(jù)的方式
今天小編就為大家分享一篇基于layui數(shù)據(jù)表格以及傳數(shù)據(jù)的方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08JavaScript限定范圍拖拽及自定義滾動條應(yīng)用(3)
這篇文章主要介紹了JavaScript限定范圍拖拽及自定義滾動條應(yīng)用的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05javascript中export?和export?default的區(qū)別
本文主要介紹了javascript中export?和export?default的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07