享元(flyweight)模式是一種用於性能優化的模式,「fly」在這裏是蒼蠅的意思,意爲蠅量級。享元模式的核心是運用共享技術來有效支持大量細粒度的對象。 若是系統中由於建立了大量相似的對象而致使內存佔用太高,享元模式就很是有用了。在 JavaScript 中,瀏覽器特別是移動端的瀏覽器分配的內存並不算多,如何節省內存就成了一件很是有意義的事情。設計模式
假設有個內衣工廠,目前的產品有 50 種男式內衣和 50 種女士內衣,爲了推銷產品,工廠決定生產一些塑料模特來穿上他們的內衣拍成廣告照片。 正常狀況下須要 50個男模特和50個女模特,而後讓他們每人分別穿上一件內衣來拍照。瀏覽器
var Model = function( sex, underwear){
this.sex = sex;
this.underwear = underwear;
};
Model.prototype.takePhoto = function(){
console.log( 'sex= ' + this.sex + ' underwear=' + this.underwear);
};
for ( var i = 1; i <= 50; i++ ){
var maleModel = new Model( 'male', 'underwear' + i );
maleModel.takePhoto();
};
for ( var j = 1; j <= 50; j++ ){
var femaleModel= new Model( 'female', 'underwear' + j );
femaleModel.takePhoto();
};
複製代碼
如上所述,如今一共有 50 種男內 衣和 50 種女內衣,因此一共會產生 100 個對象。若是未來生產了 10000 種內衣,那這個程序可能會由於存在如此多的對象已經提早崩潰。 下面咱們來考慮一下如何優化這個場景。雖然有 100 種內衣,但很顯然並不須要 50 個男 模特和 50 個女模特。其實男模特和女模特各自有一個就足夠了,他們能夠分別穿上不一樣的內衣來拍照。性能優化
/*只須要區別男女模特
那咱們先把 underwear 參數從構造函數中 移除,構造函數只接收 sex 參數*/
var Model = function( sex ){
this.sex = sex;
};
Model.prototype.takePhoto = function(){
console.log( 'sex= ' + this.sex + ' underwear=' + this.underwear);
};
/*分別建立一個男模特對象和一個女模特對象*/
var maleModel = new Model( 'male' ),
femaleModel = new Model( 'female' );
/*給男模特依次穿上全部的男裝,並進行拍照*/
for ( var i = 1; i <= 50; i++ ){
maleModel.underwear = 'underwear' + i;
maleModel.takePhoto();
};
/*給女模特依次穿上全部的女裝,並進行拍照*/
for ( var j = 1; j <= 50; j++ ){
femaleModel.underwear = 'underwear' + j;
femaleModel.takePhoto();
};
//只須要兩個對象便完成了一樣的功能
複製代碼
享元模式要求將對象的屬性劃分爲內部狀態與外部 狀態(狀態在這裏一般指屬性)。享元模式的目標是儘可能減小共享對象的數量,關於如何劃份內部狀態和外部狀態,下面的幾條經驗提供了一些指引bash
在上面的例子中,性別是內部狀態,內衣是外部狀態,經過區分這兩種狀態,大大減小了系 統中的對象數量。一般來說,內部狀態有多少種組合,系統中便最多存在多少個對象,由於性別 一般只有男女兩種,因此該內衣廠商最多隻須要 2 個對象。app
對象池是另一種性能優化方案,它跟享元模式有一些類似之處,但沒有分離內部狀態和外 部狀態這個過程。對象池維護一個裝載空閒對象的池子,若是須要對象的時候,不是直接 new,而是轉從對象池裏獲取。如 果對象池裏沒有空閒對象,則建立一個新的對象,當獲取出的對象完成它的職責以後, 再進入 池子等待被下次獲取。函數
/*通用的對象池*/
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 );
}
}
};
var iframeFactory = objectPoolFactory( function(){
var iframe = document.createElement( 'iframe' );
document.body.appendChild( iframe );
iframe.onload = function(){
iframe.onload = null; // 防止 iframe 重複加載的 bug
iframeFactory.recover( iframe );// iframe 加載完成以後回收節點
}
return iframe;
});
var iframe1 = iframeFactory.create();
iframe1.src = 'http:// baidu.com';
var iframe2 = iframeFactory.create();
iframe2.src = 'http:// QQ.com';
setTimeout(function(){
var iframe3 = iframeFactory.create();
iframe3.src = 'http:// 163.com';
}, 3000 );
複製代碼