总结JavaScript设计模式编程中的享元模式使用


Posted in Javascript onMay 21, 2016

享元模式不同于一般的设计模式,它主要用来优化程序的性能,它最适合解决大量类似的对象而产生的性能问题。享元模式通过分析应用程序的对象,将其解析为内在数据和外在数据,减少对象的数量,从而提高应用程序的性能。

基本知识

享元模式通过共享大量的细粒度的对象,减少对象的数量,从而减少对象的内存,提高应用程序的性能。其基本思想就是分解现有类似对象的组成,将其展开为可以共享的内在数据和不可共享的外在数据,我们称内在数据的对象为享元对象。通常还需要一个工厂类来维护内在数据。
在JS中,享元模式主要有下面几个角色组成:
(1)客户端:用来调用享元工厂来获取内在数据的类,通常是应用程序所需的对象,
(2)享元工厂:用来维护享元数据的类
(3)享元类:保持内在数据的类

享元模式的实现和应用

一般实现

我们举个例子进行说明:苹果公司批量生产iphone,iphone的大部分数据比如型号,屏幕都是一样,少数部分数据比如内存有分16G,32G等。未使用享元模式前,我们写代码如下:

function Iphone(model, screen, memory, SN) {
  this. model = model;
  this.screen = screen;
  this.memory = memory;
  this.SN = SN;
}
var phones = [];
for (var i = 0; i < 1000000; i++) {
  var memory = i % 2 == 0 ? 16 : 32;
  phones.push(new Iphone("iphone6s", 5.0, memory, i));
}

这段代码中,创建了一百万个iphone,每个iphone都独立申请一个内存。但是我们仔细观察可以看到,大部分iphone都是类似的,只是内存和序列号不一样,如果是一个对性能要求比较高的程序,我们就要考虑去优化它。
大量相似对象的程序,我们就可以考虑用享元模式去优化它,我们分析出大部分的iphone的型号,屏幕,内存都是一样的,那这部分数据就可以公用,就是享元模式中的内在数据,定义享元类如下:

function IphoneFlyweight(model, screen, memory) {
  this.model = model;
  this.screen = screen;
  this.memory = memory;
}

我们定义了iphone的享元类,其中包含型号,屏幕和内存三个数据。我们还需要一个享元工厂来维护这些数据:

var flyweightFactory = (function () {
  var iphones = {};
  return {
    get: function (model, screen, memory) {
      var key = model + screen + memory;
      if (!iphones[key]) {
        iphones[key] = new IphoneFlyweight(model, screen, memory);
      }
      return iphones[key];
    }
  };
})();

在这个工厂中,我们定义了一个字典来保存享元对象,提供一个方法根据参数来获取享元对象,如果字典中有则直接返回,没有则创建一个返回。
接着我们创建一个客户端类,这个客户端类就是修改自iphone类:

function Iphone(model, screen, memory, SN) {
  this.flyweight = flyweightFactory.get(model, screen, memory);
  this.SN = SN;
}

然后我们依旧像之间那样生成多个iphone

var phones = [];
for (var i = 0; i < 1000000; i++) {
  var memory = i % 2 == 0 ? 16 : 32;
  phones.push(new Iphone("iphone6s", 5.0, memory, i));
}
console.log(phones);

这里的关键就在于Iphone构造函数里面的this.flyweight = flyweightFactory.get(model, screen, memory)。这句代码通过享元工厂去获取享元数据,而在享元工厂里面,如果已经存在相同数据的对象则会直接返回对象,多个iphone对象共享这部分相同的数据,所以原本类似的数据已经大大减少,减少的内存的占用。

享元模式在DOM中的应用

享元模式的一个典型应用就是DOM事件操作,DOM事件机制分成事件冒泡和事件捕获。我们简单介绍一下这两者:
事件冒泡:绑定的事件从最里层的元素开始触发,然后冒泡到最外层
事件捕获:绑定的事件从最外层的元素开始触发,然后传到最里层
假设我们HTML中有一个菜单列表

<ul class="menu">
  <li class="item">选项1</li>
  <li class="item">选项2</li>
  <li class="item">选项3</li>
  <li class="item">选项4</li>
  <li class="item">选项5</li>
  <li class="item">选项6</li>
</ul>

点击菜单项,进行相应的操作,我们通过jQuery来绑定事件,一般会这么做:

$(".item").on("click", function () {
  console.log($(this).text());
})

给每个列表项绑定事件,点击输出相应的文本。这样看暂时没有什么问题,但是如果是一个很长的列表,尤其是在移动端特别长的列表时,就会有性能问题,因为每个项都绑定了事件,都占用了内存。但是这些事件处理程序其实都是很类似的,我们就要对其优化。

$(".menu").on("click", ".item", function () {
  console.log($(this).text());
})

通过这种方式进行事件绑定,可以减少事件处理程序的数量,这种方式叫做事件委托,也是运用了享元模式的原理。事件处理程序是公用的内在部分,每个菜单项各自的文本就是外在部分。我们简单说下事件委托的原理:点击菜单项,事件会从li元素冒泡到ul元素,我们绑定事件到ul上,实际上就绑定了一个事件,然后通过事件参数event里面的target来判断点击的具体是哪一个元素,比如低级第一个li元素,event.target就是li,这样就能拿到具体的点击元素了,就可以根据不同元素进行不同的处理。

总结

享元模式是一种优化程序性能的手段,通过共享公用数据来减少对象数量以达到优化程序的手段。享元模式适用于拥有大量类似对象并且对性能有要求的场景。因为享元模式需要分离内部和外部数据,增加了程序的逻辑复杂性,建议对性能有要求的时候才使用享元模式。

享元模式之利:
可以把网页的资源符合降低几个数量级。即使享元模式的应用无法将实例的个数削减到一个,你仍能够从中获益不少。

这种节省不需要大量修改原有代码。在创建了管理器、工厂和享元之后,就需要对代码进行的修改只不过是从直接实例化目标类改为调用管理器对象的某个方法。

享元模式之弊:
如果把它用在不必要的地方,其结果反而有损代码的运行效率。这种模式在优化代码的同时,也提高了其复杂程度,这会给调试和维护造成困难。

它之所以会妨碍调试,是因为现在可能出错的地方变成了三个:管理器、工厂和享元。

这种优化也会使维护变得更加困难。现在你面对的不是由封装着数据的对象构成的清晰架构,而是一堆又碎又乱的东西。其中的数据至少分两处保存。最好注释标明内在数据和外在数据。

只有在必要的时候才应该进行这种优化。必须在运行效率和可维护性之间进行权衡。如果拿不准是否需要使用享元模式,那么你很可能并不需要它。享元模式适合的是系统资源已经用得差不多而且明显需要进行某种优化这样一类场合。

这种模式对Javascript程序员特别有用,因为它可以用来减少网页上所要使用的DOM元素的数量,要知道这些元素需要耗费许多内存。结合使用这种模式与组合模式等组织型可以开发出功能丰富的复杂Web应用系统,它们可以平稳的运行在任何现代Javascript环境中。

享元模式的适用场合:
网页中必须使用了大量资源密集型对象。如果只会用到少许这类对象,这种优化并不划算。

对象中所保存的数据至少有一部分能被转化为外在数据。此外,将这些数据存储在对象外部所占用的资源应该相对较少,否则这种做法对于性能的提示实际上毫无意义。那种大量包含基础性代码和HTML内容的对象可能比较适合这种优化。

将外在数据分离出去后,独一无二的对象的数目相对较少。

Javascript 相关文章推荐
不安全的常用的js写法
Sep 15 Javascript
javascript学习笔记(十八) 获得页面中的元素代码
Jun 20 Javascript
网页右键ie不支持event.preventDefault和event.returnValue (需要加window)
Feb 22 Javascript
模拟多级复选框效果的jquery代码
Aug 13 Javascript
深入浅析JavaScript函数前面的加号和叹号
Jul 09 Javascript
使用jquery+iframe做一个ajax上传效果(实例)
Aug 24 jQuery
vue-resouce设置请求头的三种方法
Sep 12 Javascript
微信小程序radio组件使用详解
Jan 31 Javascript
详解React+Koa实现服务端渲染(SSR)
May 23 Javascript
详解Koa中更方便简单发送响应的方式
Jul 20 Javascript
微信小程序实现星星评价效果
Nov 02 Javascript
vue 中url 链接左边的小图标更改问题
Dec 30 Javascript
在JavaScript中模拟类(class)及类的继承关系
May 20 #Javascript
基于Node.js的JavaScript项目构建工具gulp的使用教程
May 20 #Javascript
JavaScript中用let语句声明作用域的用法讲解
May 20 #Javascript
如何用JavaScript实现动态修改CSS样式表
May 20 #Javascript
JavaScript中的跨浏览器事件操作的基本方法整理
May 20 #Javascript
用JavaScript动态建立或增加CSS样式表的实现方法
May 20 #Javascript
详解JavaScript中的事件流和事件处理程序
May 20 #Javascript
You might like
PHP中如何定义和使用常量
2013/02/28 PHP
排序算法之PHP版快速排序、冒泡排序
2014/04/09 PHP
Linux下安装oracle客户端并配置php5.3
2014/10/12 PHP
php生成唯一的订单函数分享
2015/02/02 PHP
通过修改配置真正解决php文件上传大小限制问题(nginx+php)
2015/09/23 PHP
Symfony模板的快捷变量用法实例
2016/03/17 PHP
PHP封装的page分页类定义与用法完整示例
2018/12/24 PHP
PHP常用工具函数小结【移除XSS攻击、UTF8与GBK编码转换等】
2019/04/27 PHP
jQuery AnythingSlider滑动效果插件
2010/02/07 Javascript
JavaScript 浏览器验证代码(来自discuz)
2010/07/17 Javascript
JavaScript 原型继承之构造函数继承
2011/08/26 Javascript
javascript学习笔记(七) js函数介绍
2012/06/19 Javascript
屏蔽相应键盘按钮操作
2014/03/10 Javascript
什么是Node.js?Node.js详细介绍
2014/06/01 Javascript
javascript图片滑动效果实现
2021/01/28 Javascript
jquery层级选择器的实现(匹配后代元素div)
2016/09/05 Javascript
jQuery对table表格进行增删改查
2020/12/22 Javascript
vue利用全局导航守卫作登录后跳转到未登录前指定页面的实例代码
2020/05/19 Javascript
Nodejs + sequelize 实现增删改查操作
2020/11/07 NodeJs
如何基于viewport vm适配移动端页面
2020/11/13 Javascript
[58:09]Spirit vs NB Supermajor小组赛 A组败者组决赛 BO3 第三场 6.2
2018/06/03 DOTA
将python代码和注释分离的方法
2018/04/21 Python
Python 读写文件的操作代码
2018/09/20 Python
python远程调用rpc模块xmlrpclib的方法
2019/01/11 Python
python初学者,用python实现基本的学生管理系统(python3)代码实例
2019/04/10 Python
使用python制作一个为hex文件增加版本号的脚本实例
2019/06/12 Python
tensorflow入门:TFRecordDataset变长数据的batch读取详解
2020/01/20 Python
Django中F函数的使用示例代码详解
2020/07/06 Python
HTML5单页面手势滑屏切换原理
2016/03/21 HTML / CSS
白俄罗斯在线大型超市:e-dostavka.by
2019/07/25 全球购物
春节活动策划方案
2014/01/24 职场文书
节约用水的口号
2014/06/20 职场文书
党支部群众路线整改措施思想汇报
2014/10/10 职场文书
2014年班组长工作总结
2014/11/20 职场文书
关于职业道德的心得体会
2016/01/18 职场文书
HTML5简单实现添加背景音乐的几种方法
2021/05/12 HTML / CSS