轻松掌握JavaScript享元模式

在JavaScript中,浏览器特别是移动端的浏览器分配的内存很有限,如何节省内存就成了一件非常有意义的事情。节省内存的一个有效方法是减少对象的数量。

享元模式(Flyweight),运行共享技术有效地支持大量细粒度的对象,避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类)。

享元模式可以避免大量非常相似类的开销,在程序设计中,有时需要生产大量细粒度的类实例来表示数据,如果能发现这些实例除了几个参数以外,开销基本相同的话,就可以大幅度较少需要实例化的类的数量。如果能把那些参数移动到类实例的外面,在方法调用的时候将他们传递进来,就可以通过共享大幅度第减少单个实例 的数目。

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

Flyweight中有两个重要概念--内部状态intrinsic和外部状态extrinsic之分,内部状态就是在对象里通过内部方法管理,而外部信息可以在通过外部删除或者保存。

说白点,就是先捏一个的原始模型,然后随着不同场合和环境,再产生各具特征的具体模型,很显然,在这里需要产生不同的新对象,所以Flyweight模式中常出现Factory模式,Flyweight的内部状态是用来共享的,Flyweight factory负责维护一个Flyweight pool(模式池)来存放内部状态的对象。

我们可以将内部状态相同的所有对象替换为同一个共享对象,而要创建这样一个共享对象就需要用到单例工厂方法,而不是普通的构造函数,这样做可以跟踪到已经实例化的各个对象,从而仅当所需对象的内部状态不同于已有对象时才创建一个新对象。对象的外在状态被保存在一个管理器对象中。在调用对象的方法时,管理器会把这些外在状态作为参数传入。

把一个对象的数据保存在两个不同的对象中(共享对象、管理器对象)
 1.共享对象(享元对象)
 2.单例工厂方法(创建共享对象)
 3.管理器对象(管理外部状态)

比如图书馆中的一本书可以用一个对象来表示,他有很多属性

 var Book = function( id, title, author, genre, pageCount,publisherID, ISBN, checkoutDate, checkoutMember, dueReturnDate,availability ){
  ...//初始化代码
}
Book.prototype = {
  getTitle:function(){
    return this.title;
  },
  ...
  // 更新借出状态方法
  updateCheckoutStatus:function(bookID, newStatus, checkoutDate,checkoutMember, newReturnDate){...},
  //续借
  extendCheckoutPeriod: function(bookID, newReturnDate){...},
  //是否到期
  isPastDue: function(bookID){...}
}

程序刚开始可能没问题,但是随着时间的增加,图书可能大批量增加,并且每种图书都有不同的版本和数量,你将会发现系统变得越来越慢。几千个book对象在内存里可想而知,我们需要用享元模式来优化。

我们可以将数据分成内部和外部两种数据,同一本书中,和book对象相关的数据(title,author等)可以归结为内部属性,而(checkoutMember,dueReturnDate等)可以归结为外部属性。这样,如下代码就可以在同一本书里共享同一个对象了,因为不管谁借的书,只要书是同一本书,基本信息是一样的:

 //共享对象
var Book = function(title, author, genre, pageCount, publisherID, ISBN){
  this.title = title;
  this.author = author;
  this.genre = genre;
  this.pageCount = pageCount;
  this.publisherID = publisherID;
  this.ISBN = ISBN;
};

让我们来定义一个基本工厂,用来检查之前是否创建该book的对象,如果有就返回,没有就重新创建并存储以便后面可以继续访问,这确保我们为每一种书只创建一个对象:

 /* Book工厂 单例 */
var BookFactory = (function(){
  var existingBooks = {};
  return{
    createBook: function(title, author, genre,pageCount,publisherID,ISBN){
    /*查找之前是否创建*/
      var existingBook = existingBooks[ISBN];
      if(existingBook){
        return existingBook;
      }else{
        /* 如果没有,就创建一个,然后保存*/
        var book = new Book(title, author, genre,pageCount,publisherID,ISBN);
        existingBooks[ISBN] = book;
        return book;
      }
    }
  }
});

外部状态,相对就简单了,除了我们封装好的book,其它都需要在这里管理:

 /*BookRecordManager 借书管理类 单例*/
var BookRecordManager = (function(){
  var bookRecordDatabase = {};
  return{
    /*添加借书记录*/
    addBookRecord: function(id, title, author, genre,pageCount,publisherID,ISBN, checkoutDate, checkoutMember, dueReturnDate, availability){
      var book = bookFactory.createBook(title, author, genre,pageCount,publisherID,ISBN);
      bookRecordDatabase[id] ={
        checkoutMember: checkoutMember,
        checkoutDate: checkoutDate,
        dueReturnDate: dueReturnDate,
        availability: availability,
        book: book;
      };
    },
    updateCheckoutStatus: function(bookID, newStatus, checkoutDate, checkoutMember, newReturnDate){
      var record = bookRecordDatabase[bookID];
      record.availability = newStatus;
      record.checkoutDate = checkoutDate;
      record.checkoutMember = checkoutMember;
      record.dueReturnDate = newReturnDate;
    },
    extendCheckoutPeriod: function(bookID, newReturnDate){
      bookRecordDatabase[bookID].dueReturnDate = newReturnDate;
    },
    isPastDue: function(bookID){
      var currentDate = new Date();
      return currentDate.getTime() > Date.parse(bookRecordDatabase[bookID].dueReturnDate);
    }
  };
});

通过这种方式,我们做到了将同一种图书的相同信息保存在一个bookmanager对象里,而且只保存一份;相比之前的代码,就可以发现节约了很多内存。

对象池 
对象池是另外一种性能优化方案,和享元模式有一些相似之处,但没有分离内部状态和外部状态这个过程。 
通用对象池实现:

 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; //防止iframe重复加载的bug
    iframeFactory.recover(iframe); //iframe加载完成后往对象池填回节点(收回)
  };
  return iframe;
});
//调用
var iframe1 = iframeFactory.create();
iframe1.src = 'http://www.qq.com';

参考文献: 《JavaScript模式》 《JavaScript设计模式与开发实践》

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

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

    一.定义 享元(flyweight)模式是一种用于性能优化的模式,核心是运用共享技术来有效支持大量细刻度的对象. 在JavaScript中,浏览器特别是移动端的浏览器分配的内存并不算多,如何节省内存就成了一个非常有意义的事情. 享元模式是一种用时间换空间的优化模式 内衣工厂有100种男士内衣.100中女士内衣,要求给每种内衣拍照.如果不使用享元模式则需要200个塑料模特:使用享元模式,只需要男女各1个模特. 二.什么场景下使用享元模式? (1)程序中使用大量的相似对象,造成很大的内存开销 (2)

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

    享元模式不同于一般的设计模式,它主要用来优化程序的性能,它最适合解决大量类似的对象而产生的性能问题.享元模式通过分析应用程序的对象,将其解析为内在数据和外在数据,减少对象的数量,从而提高应用程序的性能. 基本知识 享元模式通过共享大量的细粒度的对象,减少对象的数量,从而减少对象的内存,提高应用程序的性能.其基本思想就是分解现有类似对象的组成,将其展开为可以共享的内在数据和不可共享的外在数据,我们称内在数据的对象为享元对象.通常还需要一个工厂类来维护内在数据. 在JS中,享元模式主要有下面几个角色

  • 深入理解JavaScript系列(37):设计模式之享元模式详解

    介绍 享元模式(Flyweight),运行共享技术有效地支持大量细粒度的对象,避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类). 享元模式可以避免大量非常相似类的开销,在程序设计中,有时需要生产大量细粒度的类实例来表示数据,如果能发现这些实例除了几个参数以外,开销基本相同的 话,就可以大幅度较少需要实例化的类的数量.如果能把那些参数移动到类实例的外面,在方法调用的时候将他们传递进来,就可以通过共享大幅度第减少单个实例 的数目. 那么如果在JavaScript中应用享元模式

  • JS实现简单的图书馆享元模式实例

    本文实例讲述了JS实现简单的图书馆享元模式.分享给大家供大家参考.具体如下: <!DOCTYPE html> <html> <head> <title>享员模式</title> </head> <body> <script> /* *flyweight 享员模式 */ //例子是一个图书馆存书借书 ->_-> var Book = function(id, title, author, genre,

  • js设计模式之结构型享元模式详解

    运用共享技术有效地支持大量的细粒度的对象,避免对象间拥有相同内容造成多余的开销. 享元模式主要是对其数据.方法共享分离,将数据和方法分成内部数据.内部方法和外部数据.外部方法.内部方法与内部数据指的是相似或共有的数据和方法,所以将其提取出来减少开销. var Flyweight = function() { // 已创建的元素 var created = []; // 创建一个新闻包装容器 function create() { var dom = document.createElement(

  • 轻松掌握JavaScript享元模式

    在JavaScript中,浏览器特别是移动端的浏览器分配的内存很有限,如何节省内存就成了一件非常有意义的事情.节省内存的一个有效方法是减少对象的数量. 享元模式(Flyweight),运行共享技术有效地支持大量细粒度的对象,避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类). 享元模式可以避免大量非常相似类的开销,在程序设计中,有时需要生产大量细粒度的类实例来表示数据,如果能发现这些实例除了几个参数以外,开销基本相同的话,就可以大幅度较少需要实例化的类的数量.如果能把那些参

  • JavaScript享元模式原理与用法实例详解

    本文实例讲述了JavaScript享元模式原理与用法.分享给大家供大家参考,具体如下: 通过两个例子的对比来凸显享元模式的特点:享元模式是一个为了提高性能(空间复杂度)的设计模式,享元模式可以避免大量非常相似类的开销. 第一实例,没有使用享元模式,计算所花费的时间和空间使用程度. 要求为:有一个城市要进行汽车的登记 (1)汽车类 /** * 制造商 * 型号 * 拥有者 * 车牌号码 * 最近一次登记日期 */ var Car = function(make,model,year,owner,t

  • 轻松掌握Java享元模式

    定义:它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件:它适合用于只是因重复而导致使用无法令人接受的大量内存的大量物件. 特点:大大减少对象的创建,降低系统的内存,使效率提高. 企业级开发及常用框架中的应用:数据库的连接池,String的常量缓存池 具体代码实例: import java.util.HashMap; import java.util.Map; import java.util.Random; public class Demo { public stati

  • 23种设计模式(21)java享元模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述享元(Flyweight)模式的: Flyweight在拳击比赛中指最轻量级,即"蝇量级"或"雨量级",这里选择使用"享元模式"的意译,是因为这样更能反映模式的用意.享元模式是对象的结构模式.享元模式以共享的方式高效地支持大量的细粒度对象. Java中的String类型 在JAVA语言中,String类型就是使用了享元模式.String对象是final类型,对象一旦创建就不可改变.在J

  • Java设计模式之享元模式实例详解

    本文实例讲述了Java设计模式之享元模式.分享给大家供大家参考,具体如下: 解释一下概念:也就是说在一个系统中如果有多个相同的对象,那么只共享一份就可以了,不必每个都去实例化一个对象.比如说一个文本系统,每个字母定一个对象,那么大小写字母一共就是52个,那么就要定义52个对象.如果有一个1M的文本,那么字母是何其的多,如果每个字母都定义一个对象那么内存早就爆了.那么如果要是每个字母都共享一个对象,那么就大大节约了资源. 在Flyweight模式中,由于要产生各种各样的对象,所以在Flyweigh

  • JavaScript设计模式之享元模式实例详解

    本文实例讲述了JavaScript设计模式之享元模式.分享给大家供大家参考,具体如下: 通过两个例子的对比来凸显享元模式的特点:享元模式是一个为了提高性能(空间复杂度)的设计模式,享元模式可以避免大量非常相似类的开销. 第一实例,没有使用享元模式,计算所花费的时间和空间使用程度. 要求为:有一个城市要进行汽车的登记 (1)汽车类 /** * 制造商 * 型号 * 拥有者 * 车牌号码 * 最近一次登记日期 */ var Car = function(make,model,year,owner,t

  • JavaScript使用享元模式实现文件上传优化操作示例

    本文实例讲述了JavaScript使用享元模式实现文件上传优化操作.分享给大家供大家参考,具体如下: 一.享元模式是一种用于性能优化的模式,主要优化方式为,若系统中因为创建了大量类似的对象而导致内存占用过高,则可以考虑使用享元模式实现. 二.实例说明: 如果在浏览器中上传文件,若使用常规写法, 每上传一个文件,就会创建一个实例对象:如果上传2000个文件,那就有2000个对象,浏览器很可能出现假死状态.这种情况下,我们考虑适应享元模式. 三.实例: var Upload = function(

随机推荐