《JavaScript设计模式与开发实践》读书笔记之享元模式

1. 享元模式

享元模式是一种用于性能优化的模式,享元模式的核心是运用共享技术来有效支持大量细粒度的对象

1.1 传统的文件上传方法

以文件上传为例,文件上传功能可以选择依照队列,一个一个的排队上传,也支持同时选择2000个文件。
假如每一个文件都对应着一个JavaScript上传对象的创建,2000个文件就会同时创建2000个upload对象
假设这里的文件上传支持插件和flash两种

var id=0;

window.startUpload=function(uploadType,files){//uploadType区分是控件还是flash

    for(var i=0,file;file=files[i++];){

        var uploadObj=new Upload(uploadType,file.fileName,file.fileSize);

        uploadObj.init(id++);//给upload对象设置一个唯一的id

    }

};



var Upload=function(uploadType,fileName,fileSize){

    this.uploadType=uploadType;

    this.fileName=fileName;

    this.fileSize=fileSize;

    this.dom=null;

};



Upload.prototype.init=function(id){

    var that=this;

    this.id=id;

    this.dom=document.createElement('div');

    this.dom.innerHTML=

                '<span>文件名称:'+this.fileName+',文件大小:'+this.fileSize+'</span>'

                +'<button class="delFile">删除</button>';

    this.dom.querySelector('.delFile').onclick=function(){

        that.delFile();

    };

    document.body.appendChild(this.dom);

};



Upload.prototype.delFile=function(){

    return this.dom.parentNode.removeChild(this.dom);

};

接下来分别创建3个插件上传对象和3个flash上传对象

startUpload('plugin',[

    {

        fileName:'1.txt',

        fileSize:1000

    },

    {

        fileName:'2.html',

        fileSize:3000

    },

    {

        fileName:'3.txt',

        fileSize:5000

    }

]);



startUpload('flash',[

    {

        fileName:'4.txt',

        fileSize:1000

    },

    {

        fileName:'5.html',

        fileSize:3000

    },

    {

        fileName:'6.txt',

        fileSize:5000

    }

]);

这里一共有6个需要上传的文件,一共创建了6个upload对象

1.2 享元模式重构文件上传

划分内部状态和外部状态的关键主要有以下几点

  • 内部状态储存于对象内部
  • 内部状态可以被一些对象共享
  • 内部状态独立于具体的场景,通常不会改变
  • 外部状态取决于具体的场景,并根据场景而变化,外部状态不能被共享

首先,需要确认插件类型uploadType是内部状态
upload对象必须依赖uploadType属性才能工作,因为插件上传、flash上传各自调用的接口是完全不一样的
fileName和fileSize是根据场景而变化,每个文件的fileName和fileSize都不一样,fileName和fileSize没有办法被共享,它们是外部状态

 

剥离外部状态

var Upload=function(uploadType){

    this.uploadType=uploadType;

};

Upload.prototype.init函数不再需要,upload对象初始化工作被放在后面定义的uploadManager.add函数里面
接下来只需要定义Upload.prototype.delFile函数

Upload.prototype.delFile=function(id){

    uploadManager.setExternalState(id,this);

    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);

        }

    }

})();

管理器封装外部状态
完善前面提到的uploadManager对象,它负责向UploadFactory提交创建对象的请求
并用uploadDatabase对象保存所有的upload对象的外部状态

var uploadManager=(function(){

    var uploadDatabase={};

    return {

        add:function(id,uploadType,fileName,fileSize){

            var flyWeightObj=UploadFactory.create(uploadType);

            var dom=document.createElement('div');

            dom.innerHTML=

                '<span>文件名称:'+this.fileName+',文件大小:'+this.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];

            }

        }

    }

})();

触发上传动作的startUpload函数

var id=0;

window.startUpload=function(uploadType,files){

    for(var i=0,file;file=files[i++];){

        var uploadObj=uploadManager.add(++id,uploadType,file.fileName,file.fileSize);

    }

};

接下来分别创建3个插件上传对象和3个flash上传对象

startUpload('plugin',[

    {

        fileName:'1.txt',

        fileSize:1000

    },

    {

        fileName:'2.html',

        fileSize:3000

    },

    {

        fileName:'3.txt',

        fileSize:5000

    }

]);



startUpload('flash',[

    {

        fileName:'4.txt',

        fileSize:1000

    },

    {

        fileName:'5.html',

        fileSize:3000

    },

    {

        fileName:'6.txt',

        fileSize:5000

    }

]);

这里一共有6个需要上传的文件,一共创建了2个upload对象
一个是插件类型的upload对象,一个是flash类型的upload对象

1.3 对象池技术

对象池也是一种共享相关的技术,对象池维护一个装载空闲对象的池子,如果需要对象的时候,不是直接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);

        }

    }

};

利用objectPoolFactory创建一个装载iframe的对象池

var iframeFactory=objectPoolFactory(function(){

    var iframe=document.createElement('iframe');

    document.body.appendChild(iframe);

    iframe.onload=function(){

        iframe.onload=null;

        iframeFactory.recover(iframe);//加载完成后,回收节点

    }

    return iframe;

});



setTimeout(function(){

    var iframe=iframeFactory.create();

    iframe.src='http://www.qq.com';

},1000);

这里每隔1秒通过工厂方法创建一个iframe,但是采用上述对象池,始终只会生产一个iframe对象

你可能感兴趣的:(JavaScript)