探讨 JS 对象如何缓存属性的值
我们有一个这样的对象:
如果我们的业务中需要调用 foo.expensiveOperator
很多次,并且每次调用都需要耗费很多时间。这个时候我们就在想,能不能临时把这个结果缓存起来?今天我们就来讨论解决这个问题的方案。
最直接的,我们可以在此对象上定义一个 'cache' 属性,来存储我们的结果:
使用这种模式,我们已经可以避免频繁操作这个昂贵的操作了。
但是还是有个缺陷,我们外部可以随便的修改 foo.cache
的值,并不是很安全,我们希望 foo.cache
只是一个「私有属性」,只能被 foo.expensiveOperator
修改。但是 JS 的对象并不能设置私有属性,这时候我们就需要使用 Module Pattern 这个设计模式来进行封装。
其实很简单, 你大概率也见过这个设计模式,它最开始因为 Douglas Crockford 的宣传而受到关注的,在 《JavaScript 语言精粹》里面也有介绍( P40 页 )。
上面的也可以用 Class 的形式来做,但就我个人喜好而言,我并不是很喜欢 JS 的 Class 写法,我觉得它很丑陋,我更倾心于使用对象字面量(object literal notation)的形式。当然了,我没有说 Class 的写法不好,毕竟它让烦人的原型消失了:)
接下来我们看能否继续改进。
如果只写一个还好,如果我们的 foo
对象里面很多操作都是需要缓存的,那我们要针对每个操作都定义一个变量来存储缓存的值,并且还要每次都要添加判断缓存是否存在的逻辑,太麻烦了。我在项目中也是使用的这种结构,当缓存多了的时候,我明显感觉到了混乱和对未来添加更多缓存的力不从心。
下面我们来新介绍一个新的模式来解决以上的问题,也是我目前认为解决这个问题最优的方案。
首先我们定义一个它的 get
属性 ,然后呢,在第一次获取值之后使用 Object.defineProperty
API 把这个值写入这个对象。代码如下所示:
使用这个结构,我们并不需要临时变量来存缓存,我们也设置了这个属性为不可修改,防止存储的值被篡改,可能你觉得代码量很多,但是我们 defineProperty
的过程可以封装成一个函数,比如下面这样:
不过我也发现了一个缺点,那就是在这个对象存在的时候,我们想修改 expensiveOperator
存在的值得时候就不那么方便了,只好让这个对象重新生成了。但是还是不得不感叹,这种方法还是太妙了。
上面这种情况比较适合在对象的生命周期内,缓存的属性值不会发生变化的情况,对我来说,是非常适合的。
做缓存是提高我们软件性能最简单粗暴且有效的方式了,但是在具体业务场景下,每个方案各有利弊,最后还是要看情况讨论。
版权声明: 本文为 InfoQ 作者【零维】的原创文章。
原文链接:【http://xie.infoq.cn/article/faaf0c6b2c31b4e294c762287】。文章转载请联系作者。
评论