JavaScript 设计模式:享元模式详解与文件上传实战

作者:代码拾荒

时间:2026-10-29

浏览:293

本文深入解析 JavaScript 中的享元模式,阐述其通过共享技术优化大量细粒度对象内存占用的核心思想。文章详细区分了内部状态与外部状态,并结合文件上传组件的实际案例,展示如何利用工厂模式管理对象池以节省浏览器内存。同时对比了对象池与直接修改形参的区别,帮助开发者在移动端等资源受限环境下做出更优的性能决策。

文章目录

享元模式定义与核心思想

一、定义

享元(Flyweight)模式是一种专注于性能优化的设计模式,其核心机制在于运用共享技术来高效支持大量细粒度的对象。在 JavaScript 开发环境中,尤其是移动端浏览器,系统分配的内存资源相对有限,因此如何有效节省内存空间成为一个极具价值的优化方向。本质上,享元模式是一种通过增加少量时间开销来换取显著空间节省的策略。

  • 为了直观理解,可以想象一个内衣工厂拥有 100 种男士内衣和 100 种女士内衣,需要为每种内衣拍摄照片。如果不采用享元模式,则需要准备 200 个塑料模特;而应用享元模式后,仅需男女各 1 个模特即可通过更换衣物完成所有拍摄任务。

享元模式适用场景

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

该模式主要适用于以下两种情况:第一,程序中使用了大量相似对象,导致巨大的内存开销;第二,对象的大部分状态可以转化为外部状态,将这些外部状态剥离后,可以用相对少量的共享对象来替代大量对象。

享元模式的应用层面

三、如何应用享元模式?

享元模式的应用主要分为两个层面:一是在数据层,主要处理内存中大量相似的对象;二是在 DOM 层,例如在中央事件管理器中使用享元模式,避免为父容器内的每个子元素单独附加事件句柄,从而减少内存消耗。

内部状态与外部状态的划分

应用享元模式的关键在于将对象属性划分为内部状态和外部状态。内部状态是指那些独立于具体场景、通常不会改变且可以被多个对象共享的数据;而外部状态则依赖于具体场景,随场景变化而变化,且不能被共享。

在享元模式中,工厂模式(Factory Pattern)经常与之配合出现。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 程序设计有所帮助。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章