我认为我们没有直接看到更多原型模式的最大原因是 Javascript 中的默认语法是伪经典的异常,而不是更受欢迎的 Object.create。如果你想真正看到原型图案闪亮,请搜索使用此功能的地方。以下示例来自 Dojo 工具包:
警告:自从我最初写这个答案时,我就改变了对这种代码有多好的想法。虽然基本思想仍然存在,但您应该小心,您有改变实例(“this”)属性的方法。这是因为如果您通过委托人调用委托对象中的方法,那么您最终可能会在委托人而不是委托人中设置变量,并且如果其他人最终直接访问委托人,这可能会破坏一些不变量。
虽然你有不可变的对象,但整个想法是 100% 没问题的。
Dojo 定义了一个通用的store 接口(使用 get()、add() 等方法),例如,可用于从服务器抽象一个 REST API。我们想创建一个Cache 函数,它接收任何数据存储并返回一个新版本,该版本缓存对 get() 方法的任何调用(这允许我们将缓存与实现实际 get() 的特定于存储的行为分离)
第一个想法是使用 Javascript 高度动态的事实来替换 get 方法:
//not the actual implementation. Things get more complicated w/ async code.
var oldGet = store.get;
store.get = function(id){
if(!cache[id]){ cache[id] = oldGet(id); }
return cache[id];
}
但是,这会破坏原始对象,因此您无法再访问原始方法,并且还使得并行添加其他修改变得更加棘手。
第二个想法是使用委托制作更强大的解决方案:
function Cache(obj){
this.obj = obj;
}
Cache.prototype = {
get: function(){
//do things involving this.obj...
}
};
这看起来很有希望,直到您记得生成的 Cache 对象需要实现存储接口。我们可以尝试手动添加所有方法:
Cache.prototype = {
//...
put: function(){ return this.obj.apply(this, arguments); },
//...
}
但这不仅麻烦而且容易出错(它很容易忘记一些东西),它甚至不如对象修改解决方案强大,因为我们无法访问原始对象中的方法,而这些方法不是来自商店界面。
好吧,实现这种“自动委托”的方法是继承,但在这种情况下,它最初似乎没用,因为您需要为每个可能的存储类创建一个新的缓存子类,或者您需要某种花哨的多继承混合。输入原型继承以节省时间。我们可以轻松地创建一个新对象,为旧对象添加功能而无需修改它或不必摆弄类层次结构
dojo.store.Cache = function(masterStore, cachingStore, options){
//...
return dojo.delegate(masterStore, {
//...
get: function(id, directives){
//...
}
//...
}
}
其中 dojo.delegate 是一个函数,它使用第二个参数中的所有属性创建一个新对象,其原型将是第一个参数。
非 JS 理论杂谈:原型继承可以在更多委托场景中更积极地使用,如 Self 允许多个原型以及在运行时直接访问和修改原型的语言。例如,可以通过将所有合适的方法委托给原型并在状态发生变化时更改原型来实现 GoF 中的状态模式。