一、定義
享元(flyweight)模式是一種用於效能最佳化的模式,核心是運用共用技術來有效支援大量細刻度的對象。
在JavaScript中,瀏覽器特別是移動端的瀏覽器分配的記憶體並不算多,如何節省記憶體就成了一個非常有意義的事情。
享元模式是一種用時間換空間的最佳化模式
- 內衣工廠有100種男士內衣、100中女士內衣,要求給每種內衣拍照。如果不使用享元模式則需要200個塑料模特;使用享元模式,只需要男女各1個模特。
二、什麼情境下使用享元模式?
(1)程式中使用大量的相似對象,造成很大的記憶體開銷
(2)對象的大多數狀態都可以變為外部狀態,剝離外部狀態之後,可以用相對較少的共用對象取代大量對象
三、如何應用享元模式?
第一種是應用在資料層上,主要是應用在記憶體裡大量相似的對象上;
第二種是應用在DOM層上,享元可以用在中央事件管理器上用來避免給父容器裡的每個子項目都附加事件控制代碼。
享元模式要求將對象的屬性分為內部狀態和外部狀態。
內部狀態獨立於具體的情境,通常不會改變,可以被一些對象共用;
外部狀態取決於具體的情境,並根據情境而變化,外部狀態不能被共用。
享元模式中常出現原廠模式,Flyweight的內部狀態是用來共用的,Flyweight factory負責維護一個Flyweight pool(模式池)來存放內部狀態的對象。
缺點:對象數量少的情況,可能會增大系統的開銷,實現的複雜度較大!
四、樣本:檔案上傳
var Upload = function(uploadType) { this.uploadType = uploadType;}/* 刪除檔案(內部狀態) */Upload.prototype.delFile = function(id) { uploadManger.setExternalState(id, this); // 把當前id對應的外部狀態都組裝到共用對象中 // 大於3000k提示 if(this.fileSize < 3000) { return this.dom.parentNode.removeChild(this.dom); } if(window.confirm("確定要刪除檔案嗎?" + this.fileName)) { return this.dom.parentNode.removeChild(this.dom); }}/** 工廠對象執行個體化 * 如果某種內部狀態的共用對象已經被建立過,那麼直接返回這個對象 * 否則,建立一個新的對象 */var UploadFactory = (function() { var createdFlyWeightObjs = {}; return { create: function(uploadType) { if(createdFlyWeightObjs[uploadType]) { return createdFlyWeightObjs[uploadType]; } return createdFlyWeightObjs[uploadType] = new Upload(uploadType); } };})();/* 管理器封裝外部狀態 */var uploadManger = (function() { var uploadDatabase = {}; return { add: function(id, uploadType, fileName, fileSize) { var flyWeightObj = UploadFactory.create(uploadType); var dom = document.createElement('div'); dom.innerHTML = "<span>檔案名稱:" + fileName + ",檔案大小:" + fileSize +"</span>" + "<button class='delFile'>刪除</button>"; dom.querySelector(".delFile").onclick = function() { flyWeightObj.delFile(id); }; document.body.appendChild(dom); uploadDatabase[id] = { fileName: fileName, fileSize: fileSize, dom: dom }; return flyWeightObj; }, setExternalState: function(id, flyWeightObj) { var uploadData = uploadDatabase[id]; for(var i in uploadData) { // 直接改變形參(新思路!!) flyWeightObj[i] = uploadData[i]; } } };})();/*觸發上傳動作*/var id = 0;window.startUpload = function(uploadType, files) { for(var i=0,file; file = files[i++];) { var uploadObj = uploadManger.add(++id, uploadType, file.fileName, file.fileSize); }};/* 測試 */startUpload("plugin", [ { fileName: '1.txt', fileSize: 1000 },{ fileName: '2.txt', fileSize: 3000 },{ fileName: '3.txt', fileSize: 5000 }]);startUpload("flash", [ { fileName: '4.txt', fileSize: 1000 },{ fileName: '5.txt', fileSize: 3000 },{ fileName: '6.txt', fileSize: 5000 }]);
五、補充
(1)直接改變形參Demo
function f1() { var obj = {a: 1}; f2(obj); console.log(obj); // {a: 1, b: 2}}function f2(obj) { obj.b = 2;}f1();
(2)對象池,也是一種效能最佳化方案,其跟享元模式有一些相似之處,但沒有分離內部狀態和外部狀態的過程。
var objectPoolFactory = function(createObjFn) { var objectPool = []; return { create: function() { var obj = objectPool.lenght === 0 ? createObjFn.apply(this, arguments) : objectPool.shift(); return obj; }, recover: function() { objectPool.push(obj); } };}
希望本文所述對大家學習javascript程式設計有所協助。