《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对象

原文地址:https://www.cnblogs.com/GongQi/p/4663078.html