### 一、定義
享元(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);
}
};
}
~~~
轉載請標明出處:[http://blog.csdn.net/ligang2585116](http://blog.csdn.net/ligang2585116)