本节书摘来华章计算机出版社《JavaScript应用程序设计》一书中的第3章,第3.6节,作者:Eric Elliott 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
3.6 享元模式
比起为每个实例分别创建一组实例数据,享元模式通过将可重复使用的方法与属性保存在一个代理对象中,从而节省了系统资源。特别是当应用中存在大量类型相同的实例时,使用享元模式可以明显提升系统性能并有效减少内存损耗。
在其他语言中,你需要额外的步骤来配置享元模式,比如说首先创建一个代理对象,再将所有方法链接至该对象上做延后执行。在JavaScript中,代理原型是一种天然的内建代理对象,所以你不用自己再花费精力实现一个享元模式。
假设你正在编写一个视频游戏,在游戏中会有成百上千个“敌人”对象,每一个“敌人”对象封装着诸如力量、速度、攻击、防御等一组通用属性与方法,同时包含了当前敌人的位置信息与生命值。在JavaScript中,你可以将这些属性与方法放在对象的原型上维护,让位置信息与生命值的变更仅作用在实例层面,而让所有通用属性与方法的变更作用在原型层面。
var enemyPrototype = {
name: 'Wolf',
position: { // Override this with setPosition
x: 0,
y: 0
},
setPosition: function setPosition (x, y) {
this.position = {
x: x,
y: y
};
return this;
},
health: 20, // Overrides automatically on change
bite: function bite() {
},
evade: function evade() {
}
},
spawnEnemy = function () {
return Object.create(enemyPrototype);
};
test('Flyweight pattern.', function () {
var wolf1 = spawnEnemy(),
wolf2 = spawnEnemy();
wolf1.health = 5;
ok(wolf2.health = 20,
'Primitives override automatically.');
ok(wolf1.setPosition(10, 10)
.position.x === 10, 'Object override works.');
equal(wolf2.position.x, 0,
'The prototype should remain unchanged.');
});
JavaScript天生就具备在原型上存放数据的能力,所以从理论上来说,对象的所有方法都适用于享元模式,所以你会看到类似这样的代码:
MyConstructor.prototype.myMethod = function () {
// A method to be shared...
};
将实例级别的数据存放在原型上较为少见,一些复用性较强的默认属性值比较适合放在原型上做托管。为了避免在修改实例数据时出现安全性问题,在对数组与对象这类引用类属性操作时,需格外小心,尽量对它们做替换,而不是修改。