• JavaScript享元模式


          享元(flyweight----蝇量级)模式是一种用于性能优化的模式。享元模式的核心是运用共享技术来有效支持大量细粒度的对象。如果系统中因为创建了大量类似的对象而导致内存占用过高,享元模式就很有用了。在JavaScript中,浏览器特别是移动端的浏览器分配的内存并不算多,如何节省内存就成了一件非常有意义的事。

          享元模式要求将对象的属性划分为内部状态与外部状态(在这里状态是指属性)。享元模式的目标是尽量减少共享对象的数量。如何划分内部状态和外部状态,可以参考一下经验:
                1.内部状态存储于对象内部
                2.内部状态可以被一些对象共享
                3.内部状态独立于具体的场景,通常不会改变
                4.外部状态取决于具体的场景,并根据场景而变化,外部状态不能被共享

          使用享元模式的关键就是区别内部状态和外部状态。

          使用了享元模式后,会因为要维护一些多出来的对象,造成额外的开销。但它又有它的好处。享元模式带来的好处很大程度上取决于如何使用及何时使用,一般来说,一下情况发生时便可以使用享元模式:
                1.一个程序中使用了大量的相似对象
                2.由于使用了大量对象,造成很大的内存开销
                3.对象的大多数状态都可以变为外部状态
                4.剥离出对象的外部状态后,可以用相对较少的共享对象取代大量对象

          实例:微云上传文件
          微云支持好几种上传方式,比如浏览器插件、Flash和表单上传等,为了简化例子,假设只有插件和Flash这两种。不论是插件上传,还是Flash上传,原理都是一样的,当用户选择了文件之后,插件和Flash都会通知调用window下的一个全局JavaScript函数startUpload,用户选择的文件列表被组合成一个数组files塞进该函数的参数列表里:

          var id = 0;

          window.startUpload = function(uploadTypd, 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(){
                 if( this.fileSize < 3000 ){
                      return this.dom.parentNode.removeChild( this.dom );
                 }
                 if( window.confirm( '确定要删除该文件吗?' + this.fileName ) ){
                      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
                 }
             ]);

          我们需要确认插件类型uploadType是内部状态,其它则是外部状态,这样的话上面的代码可以重构,如下:

         var Upload = function( uploadType ){
               this.uploadType = uploadType;
         };

         Upload.prototype.delFile = function( id ){
               uploadManager.setExternalState( id, this ); //把当前ID对应的对象的外部状态都组装到共享对象中

               if( this.fileSize < 3000 ){
                   return this.dom.parentNode.removeChild( this.dom );
               }
               if( window.confirm( '确定要删除该文件吗?' + this.fileName ) ){
                   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 );
                  }
              };
          })();

           var uploadManager = (function(){
                var uploadDatabase = {};

                return {
                     add: function( id, uploadTypd, 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( this.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 ];
                            }
                      }
                };
           })();


             var id = 0;

             window.startUpload = function( uploadTypd, files ){
                    for(var i=0, file; file = files[ i++]; ){
                         var uploadObj = new Upload( ++id, uploadType, file.fileName, file.fileSize );
                    }
              }

      

           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个upload对象,重构后,对象的数量减少为2,而且就算现在同时上传2000个文件,维持的对象也只是两个。

          享元模式的关键就是把内部状态和外部状态分离开来。有多少种内部状态的组合,系统中变最多存在多少个共享对象,而外部状态存储在共享对象的外部,在必要时被传入共享对象来组装成一个完整的对象。如果没有内部状态,那么共享对象就是一个单例。如果没有外部状态,像字符串的对象池,就不完全是享元模式。

  • 相关阅读:
    bzoj1731 [Usaco2005 dec]Layout 排队布局
    loj10087 Intervals
    差分约束小结
    bzoj1112 [POI2008]砖块Klo
    bzoj3524 [POI2014]Couriers
    poj2752 Seek the Name, Seek the Fame
    1027C Minimum Value Rectangle
    bzoj2212 [POI2011]Tree Rotations
    bzoj3747 [POI2015]Kinoman
    628D Magic Numbers
  • 原文地址:https://www.cnblogs.com/xbj-2016/p/5828701.html
Copyright © 2020-2023  润新知