学习JavaScript设计模式之享元模式


Posted in Javascript onJanuary 18, 2016

一、定义

享元(flyweight)模式是一种用于性能优化的模式,核心是运用共享技术来有效支持大量细刻度的对象。
在JavaScript中,浏览器特别是移动端的浏览器分配的内存并不算多,如何节省内存就成了一个非常有意义的事情。
享元模式是一种用时间换空间的优化模式

  • 内衣工厂有100种男士内衣、100中女士内衣,要求给每种内衣拍照。如果不使用享元模式则需要200个塑料模特;使用享元模式,只需要男女各1个模特。

二、什么场景下使用享元模式?

(1)程序中使用大量的相似对象,造成很大的内存开销
(2)对象的大多数状态都可以变为外部状态,剥离外部状态之后,可以用相对较少的共享对象取代大量对象

三、如何应用享元模式?

第一种是应用在数据层上,主要是应用在内存里大量相似的对象上;
第二种是应用在DOM层上,享元可以用在中央事件管理器上用来避免给父容器里的每个子元素都附加事件句柄。

享元模式要求将对象的属性分为内部状态外部状态
内部状态独立于具体的场景,通常不会改变,可以被一些对象共享;
外部状态取决于具体的场景,并根据场景而变化,外部状态不能被共享。

享元模式中常出现工厂模式,Flyweight的内部状态是用来共享的,Flyweight factory负责维护一个Flyweight pool(模式池)来存放内部状态的对象。

缺点:对象数量少的情况,可能会增大系统的开销,实现的复杂度较大!

四、示例:文件上传

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

/* 删除文件(内部状态) */
Upload.prototype.delFile = function(id) {
  uploadManger.setExternalState(id, this);  // 把当前id对应的外部状态都组装到共享对象中
  // 大于3000k提示
  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 uploadManger = (function() {
  var uploadDatabase = {};

  return {
    add: function(id, uploadType, fileName, fileSize) {
      var flyWeightObj = UploadFactory.create(uploadType);
      var dom = document.createElement('div');
      dom.innerHTML = "<span>文件名称:" + fileName + ",文件大小:" + 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];
      }
    }
  };
})();

/*触发上传动作*/
var id = 0;
window.startUpload = function(uploadType, files) {
  for(var i=0,file; file = files[i++];) {
    var uploadObj = uploadManger.add(++id, uploadType, file.fileName, file.fileSize);
  }
};

/* 测试 */
startUpload("plugin", [
  {
    fileName: '1.txt',
    fileSize: 1000
  },{
    fileName: '2.txt',
    fileSize: 3000
  },{
    fileName: '3.txt',
    fileSize: 5000
  }
]);
startUpload("flash", [
  {
    fileName: '4.txt',
    fileSize: 1000
  },{
    fileName: '5.txt',
    fileSize: 3000
  },{
    fileName: '6.txt',
    fileSize: 5000
  }
]);

五、补充

(1)直接改变形参Demo

function f1() {
  var obj = {a: 1};
  f2(obj);
  console.log(obj);  // {a: 1, b: 2}
}
function f2(obj) {
  obj.b = 2;
}
f1();

(2)对象池,也是一种性能优化方案,其跟享元模式有一些相似之处,但没有分离内部状态和外部状态的过程。

var objectPoolFactory = function(createObjFn) {
  var objectPool = [];
  return {
    create: function() {
      var obj = objectPool.lenght === 0 ? createObjFn.apply(this, arguments) : objectPool.shift();
      return obj;
    },
    recover: function() {
      objectPool.push(obj);
    }
  };
}

希望本文所述对大家学习javascript程序设计有所帮助。

Javascript 相关文章推荐
关于JavaScript的with 语句的使用方法
May 09 Javascript
js获取时间(本周、本季度、本月..)
Nov 22 Javascript
用javascript为页面添加天气显示实现思路及代码
Dec 02 Javascript
Bootstrap布局方式详解
May 27 Javascript
基于JavaScript实现购物网站商品放大镜效果
Sep 06 Javascript
Vue.js绑定HTML class数组语法错误的原因分析
Oct 19 Javascript
JS非行间样式获取函数的实例代码
Jun 05 Javascript
使用JavaScript中的lodash编写双色球效果
Jun 24 Javascript
在create-react-app中使用css modules的示例代码
Jul 31 Javascript
Nuxt.js 数据双向绑定的实现
Feb 17 Javascript
详解es6新增数组方法简便了哪些操作
May 09 Javascript
javascript实现简单留言板案例
Feb 09 Javascript
纯JavaScript基于notie.js插件实现消息提示特效
Jan 18 #Javascript
学习JavaScript设计模式之责任链模式
Jan 18 #Javascript
深入学习jQuery Validate表单验证(二)
Jan 18 #Javascript
深入学习jQuery Validate表单验证
Jan 18 #Javascript
jQuery Validate表单验证插件 添加class属性形式的校验
Jan 18 #Javascript
图片旋转、鼠标滚轮缩放、镜像、切换图片js代码
Dec 13 #Javascript
理解JavaScript中Promise的使用
Jan 18 #Javascript
You might like
PHP中的超全局变量
2006/10/09 PHP
关于PHP的相似度计算函数:levenshtein的使用介绍
2013/04/15 PHP
在Laravel框架里实现发送邮件实例(邮箱验证)
2016/05/20 PHP
PHP PDOStatement::getAttribute讲解
2019/02/01 PHP
PHP优化之批量操作MySQL实例分析
2020/04/23 PHP
jQuery UI-Draggable 参数集合
2010/01/10 Javascript
js下判断 iframe 是否加载完成的完美方法
2010/10/26 Javascript
javascript parseInt() 函数的进制转换注意细节
2013/01/08 Javascript
js 限制input只能输入数字、字母和汉字等等
2013/12/18 Javascript
node.js中的fs.chownSync方法使用说明
2014/12/16 Javascript
JS根据生日算年龄的方法
2015/05/05 Javascript
原生js获取元素样式的简单方法
2016/08/06 Javascript
js正则表达式最长匹配(贪婪匹配)和最短匹配(懒惰匹配)用法分析
2016/12/27 Javascript
微信小程序 input输入框详解及简单实例
2017/01/10 Javascript
JavaScript使用链式方法封装jQuery中CSS()方法示例
2017/04/07 jQuery
js实现简易聊天对话框
2017/08/17 Javascript
对layui中表单元素的使用详解
2018/08/15 Javascript
vuex的module模块用法示例
2018/11/12 Javascript
详解vue中使用微信jssdk
2019/04/19 Javascript
微信小程序缓存过期时间的使用详情
2019/05/12 Javascript
解决layer.open弹出框不能获取input框的值为空的问题
2019/09/10 Javascript
jQuery实现简单日历效果
2020/07/05 jQuery
JavaScript中的执行环境和作用域链
2020/09/04 Javascript
[36:13]Mineski vs iG 2018国际邀请赛小组赛BO2 第一场 8.16
2018/08/17 DOTA
python分析网页上所有超链接的方法
2015/05/08 Python
PyCharm安装第三方库如Requests的图文教程
2018/05/18 Python
Python内置函数locals和globals对比
2020/04/28 Python
python调用私有属性的方法总结
2020/07/24 Python
加拿大最大的相机店:Henry’s
2017/05/17 全球购物
三星印度官网:Samsung印度
2019/08/03 全球购物
医疗纠纷协议书
2014/04/16 职场文书
庆七一活动总结
2014/08/27 职场文书
银行柜员工作心得体会
2016/01/23 职场文书
深度学习详解之初试机器学习
2021/04/14 Python
将Python代码打包成.exe可执行文件的完整步骤
2021/05/12 Python
JavaScript正则表达式实现注册信息校验功能
2022/05/30 Java/Android