本节书摘来华章计算机出版社《JavaScript应用程序设计》一书中的第3章,第3.6节,作者:Eric Elliott 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
比起为每个实例分别创建一组实例数据,享元模式通过将可重复使用的方法与属性保存在一个代理对象中,从而节省了系统资源。特别是当应用中存在大量类型相同的实例时,使用享元模式可以明显提升系统性能并有效减少内存损耗。在其他语言中,你需要额外的步骤来配置享元模式,比如说首先创建一个代理对象,再将所有方法链接至该对象上做延后执行。在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... };将实例级别的数据存放在原型上较为少见,一些复用性较强的默认属性值比较适合放在原型上做托管。为了避免在修改实例数据时出现安全性问题,在对数组与对象这类引用类属性操作时,需格外小心,尽量对它们做替换,而不是修改。
相关资源:JavaScript高级程序设计(附源码)