zoukankan      html  css  js  c++  java
  • 《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对象

  • 相关阅读:
    第108题:将有序数组转换成二叉搜索树
    第107题:二叉树的层次遍历II
    第106题:从中序与后序遍历序列构造二叉树
    java类读取properties文件
    WdatePicker.js开始日期和结束日期比较
    对两个整数变量的值进行互换
    Java基础知识总结
    jdk环境变量
    逻辑运算符有什么用?
    if和switch的应用
  • 原文地址:https://www.cnblogs.com/GongQi/p/4663078.html
Copyright © 2011-2022 走看看