JS享元模式

享元模式

内部状态与外部状态
内部状态存储于对象内部。
 内部状态可以被一些对象共享。
 内部状态独立于具体的场景,通常不会改变。
 外部状态取决于具体的场景,并根据场景而变化,外部状态不能被共享。

内部状态与外部状态分离
例子:通过plugin或flash两种方式上传文件l
首先会获得需要的特定内部状态的对象,如果已在createdFlyWeightObjs对象属性中,则直接返回,否则new Upload,并放入createdFlyWeightObjs对象中已备用,之后再设置目标对象的外部状态就生成了想要的对象

  • 剥离外部状态
    明确了 uploadType 作为内部状态之后,我们再把其他的外部状态从构造函数中抽离出来,Upload 构造函数中只保留 uploadType 参数:
var Upload = function (uploadType) {
    this.uploadType = uploadType;
};
Upload.prototype.delFile = function (id) {
    uploadManager.setExternalState(id, this); // (1)
    if (this.fileSize < 3000) {
        return this.dom.parentNode.removeChild(this.dom);
    }
    if (window.confirm('确定要删除该文件吗? ' + this.fileName)) {
        return this.dom.parentNode.removeChild(this.dom);
    }
};

在开始删除文件之前,需要读取文件的实际大小,而文件的实际大小被储存在外部管理器uploadManager 中,所以在这里需要通uploadManager.setExternalState 方法给共享对象设置正确的 fileSize ,上段代码中的(1)处表示把当前 id 对应的对象的外部状态都组装到共享对象中。

  • 工厂进行对象实例化(内部状态)
    接下来定义一个工厂来创建 upload 对象,如果某种内部状态对应的共享对象已经被创建过,那么直接返回这个对象,否则创建一个新的对象:
var UploadFactory = (function () {
    var createdFlyWeightObjs = {};
    return {
        create: function (uploadType) {
            if (createdFlyWeightObjs[uploadType]) {
                return createdFlyWeightObjs[uploadType];
            }
            return createdFlyWeightObjs[uploadType] = new Upload(uploadType);
        }
    }
})();
  • 管理器封装外部状态(外部状态)
    现在我们来完善前面提到的 uploadManager 对象,它负责向 UploadFactory 提交创建对象的请求,并用一个 uploadDatabase 对象保存所有 upload 对象的外部状态,以便在程序运行过程中给upload 共享对象设置外部状态,代码如下:
var uploadManager = (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];
            }
        }
    }
})();

然后是开始触发上传动作的 startUpload 函数:

var id = 0;
window.startUpload = function (uploadType, files) {
    for (var i = 0, file; file = files[i++];) {
        var uploadObj = uploadManager.add(++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
    }
]);

对象池
对象池,获取对象时先检查对象池里面有没有所需对象,如果有直接返回一个,没有则创建一个。
我们还可以在对象池工厂里,把创建对象的具体过程封装起来,实现一个通用的对象池:

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:// baidu.com';
var iframe2 = iframeFactory.create();
iframe2.src = 'http:// QQ.com';
setTimeout(function () {
    var iframe3 = iframeFactory.create();
    iframe3.src = 'http:// 163.com';
}, 3000);

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值