享元(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 );
复制代码