你不知道的javascript设计模式(十二) ----享元模式

前言

        同学们,上一章我们介绍了模板方法模式,这是一种基于继承的设计模式,通过设计抽象类可以避免类似类的重复定义,并且因为js的特殊原型克隆,我们也就js的实现方式进一步说明了模板方法模式,今天我们将进一步介绍一种新的设计模式,享元模式。

正文

享元模式的定义

        享元模式是一种用于性能优化的模式,享元模式的核心是利用共享技术来有效支持大量细粒度的对象。尤其是对于javascript,浏览器所能分配的内存并不多,所以怎样节省内存就显得很有意义
        举一个例子方便大家理解,假设有个衣服工厂,有50套男士衣服和50套女士衣服,这时候工厂需要生产塑料模特来穿这些衣服用于宣传,如果用程序来实现,不用享元模式的情况下,我们可能就会创建100个对象来穿这些衣服,但是随着衣服的增多,创建的对象也会越来越多,导致最后消耗的资源难以承受
        换个思路想一想,如果我们不创建100个对象呢,反正男士的衣服可以用一套男士模特来试穿,而女士只需要一套女士模特来试穿,我们是不是只需要创建两个对象就行呢
        这就是最简单的一个享元模式的思想,通过抽取共同点,并且用不同点来区分,尽量减少创建消耗的资源,达到一个公用的目的

内部状态与外部状态

        享元模式要求将对象的属性划分为内部状态和外部状态,上一节我们说到享元模式的目标就是为了尽量减少共享对象的数量,关于怎么区分内部状态和外部状态,遵循以下原则:

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

        在上面的例子中,性别就是内部对象,因为我们根据性别来区别出了两种模特,而衣服类型就是外部状态,因为可以有各种各样的衣服可以选择。所以,通常来说,内部对象有多少种组合,享元模式就需要至少创建多少对象

享元模式的实现

        还是之前的例子,我们先来看看享元模式直接实现这个衣服工厂应该怎么实现

var Model = function(sex, underwear) {
	this.sex = sex;
	this.underwear = underwear;
}
Model.prototype.takePhoto = function() {
	console.log(`给${this.sex}模特拍了一张${this.underwear}照`);
}
for(let i = 0; i < 50; i++) {
	var manModel = new Model(‘man', 'underwear' + i);
	manModel.takePhoto();
}
for(let i = 0; i < 50; i++) {
	var womanModel = new Model(‘woman', 'underwear' + i);
	womanModel.takePhoto();
}

        这样为这100件衣服我们就创建出了100个对象,如果衣服再多一点比如1000件,谷歌火狐这些浏览器还可以勉强支持,ie浏览器直接就当场去世了,然后我们换成之前说的享元模式的方法来实现,sex作为内部属性,underwear作为外部属性

var Model = function(sex) {
	this.sex = sex;
}
var manModel = new Model('man'),
	womanModel = new Model('woman');
Model.prototype.takePhoto = function() {
	console.log(`给${this.sex}模特拍了一张${this.underwear}照`);
}
for(let i = 0; i < 50; i++) {
	manModel.underwear = ‘underwear' + i;
	manModel.takePhoto();
}
for(let i = 0; i < 50; i++) {
	womanModel.underwear = ‘underwear' + i;
	womanModel.takePhoto();
}

        这样,我们通过享元模式只需要创建2个对象就可以实现与第一种方法相同的效果了

对象池

        提了享元模式,就不得不谈一下对象池的内容了,什么是对象池呢,假设一个图书馆,里面现在有三本红宝书,有读者来借书的时候,如果还有,直接借给读者就是,如果这时候有四名读者来借红宝书,三本就不够用了,图书馆不得不去再买一本来借给第四名读者。有则取,无则造,这就是对象池。根据上面的思路我们不难实现一个通用的对象池

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);
		}
	}
}

        利用这个对象池我们可以很轻松地创建一个Model类型的对象池,来保证只要塑料模特够用,咱就不创建新的

var ModelPool = objectPoolFactory(function() {
    var model = new Model();
    ModelPool.recover(model);
    return model;
})

        有了这个对象池,上面的案例我们就可以这么实现

for(let i = 0; i < 50; i++) {
	var manModel = ModelPool.create();
	manModel.sex = 'man';
	manModel.underwear =  'underwear' + i;
	manModel.takePhoto();
}
for(let i = 0; i < 50; i++) {
	var womanModel = ModelPool.create();
	womanModel.sex = 'woman';
	womanModel.underwear =  'underwear' + i;
	womanModel.takePhoto();
}

        这样做的好处就是,从头到尾,其实就创建了一个模特对象而已,后面的create一直在复用之前的对象

对象池与享元模式的区别

        对象池与享元模式有很大的相似处,都是复用对象,但是其实两者是有本质上的区别的。

  • 池化技术中的“复用”可以理解为“重复使用”,主要目的是节省时间(比如从对象池中取一个对象,不需要重新创建)。在任意时刻,每一个对象、连接、线程,并不会被多处使用,而是被一个使用者独占,当使用完成之后,放回到池中,再由其他使用者重复利用。
  • 享元模式中的“复用”可以理解为“共享使用”,在整个生命周期中,都是被所有使用者共享的,主要目的是节省空间。

        我们可以很明显发现,在上面对象池的例子中,并没有分离内部状态和外部状态的过程,所以对象池与享元模式是完全不可同日而语的。

小结

        这一章我们介绍了享元模式,当在业务逻辑中需要创建大量对象的时候,我们就可以考虑利用享元模式,分离这些对象的内部和外部状态,实现对象间的复用,从而减少重复资源上的消耗,我们还介绍了对象池,对象池与享元模式有相似之处,同样是为了实现对象的复用,但是与享元模式不同,在复用对象被使用的时候,是处于被独占的状态,且也没有对内部和外部状态的区分,要着重注意两者的不同
       小伙伴们今天的学习就到这里了,如果觉得本文对你有帮助的话,欢迎转发,评论,收藏,点赞!!!
       每天学习进步一点点,就是领先的开始。如果想继续提高,欢迎关注我,或者关注公众号”祯民讲前端“。大量前端技术文章,面试资料,技巧等助你更进一步!
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值