《103010》的作者彼得高顺曾经说过,如果你需要一个模式,那一定有问题。他说的是,由于语言的固有缺陷,他不得不寻求和总结一个通用的解决方案。
无论是弱类型还是强类型,静态还是动态语言,命令式还是声明式语言,每种语言都有其固有的优点和缺点。牙买加运动员在短跑甚至拳击方面有一些优势,但在练习瑜伽方面缺乏一些。
术士和暗影牧师可以轻松成为优秀的助手,而一个飞在梅肯地图后面的敌人则会略显尴尬。在程序中,用静态语言实现decorator可能要花很多功夫,但是js可以随时在对象上抛出方法,这样decorator模式就成了js中的鸡肋。
关于Javascript设计模式的书很少,《Practical Common Lisp》是经典的一本,但是里面的例子比较啰嗦,所以结合我工作中写的代码总结一下我的理解。如果我的理解有任何偏差,请随时纠正我。
一.单一模式
singleton pattern的定义是产生类的唯一实例,但是js本身是一种无类语言。许多关于js设计模式的文章将{}作为一个单独的案例,这几乎没有意义。因为js有很多生成对象的方法,让我们来看看另一个更有意义的单一案例。
有一个共同的要求,即当单击按钮时,页面上应该弹出一个遮罩层。例如,当web.qq.com点击登录。
这个生成灰色背景遮罩层的代码很容易写。
var create mask=function(){ return document,body . appendchild(document . createelement(div));}$(“按钮”)。单击(function(){ Var mask=create mask();mask . show();})问题是这个遮罩层是全局唯一的,所以每次调用createMask都会创建一个新的div。虽然可以从隐藏掩膜层中去除,但这样做显然是不合理的。
看第二个方案,在页面的开头创建div,然后用变量引用它。
var mask=document . body . appendchild(document . create element(' div '));$(' '按钮')。单击(function(){ mask . show();})诚然,页面上只会创建一个掩膜层div,但随之而来的是另一个问题。也许我们永远都不需要这个遮罩层,然后我们会浪费一个div。dom节点上的任何操作都应该非常吝啬。
如果你能使用一个变量。判断div是否已经创建?
var掩码;var create mask=function(){ if(mask)返回mask;else{mask=document,body . appendchild(document . create element(div));返回掩码;}}看起来不错,这里确实完成了一个生成单列对象的函数。让我们仔细看看这段代码有什么问题。
首先,这个功能有一些副作用。函数体中外部变量掩码的引用已被更改。在多人协作项目中,createmask是一个不安全的功能。另一方面,全局变量掩码不是必需的。让我们改进一下。
var create mask=function(){ var mask;return function(){ return mask | |(mask=document . body . append child(document . create element(' div ')))}()用一个简单的闭包包装变量掩码,这个闭包至少对createMask函数是关闭的。
可能看到这里,觉得单体模式太简单了。的确,有些设计模式非常简单。即使我从不关注设计模式的概念,我也会在平时的代码中不自觉地使用一些设计模式。就像很多年前,当我明白老人的车是什么的时候,我也以为尼玛原来是老人的车。
GOF的23种设计模式早已存在,并在软件开发中反复使用。如果程序员没有清楚地意识到自己使用了一些模式,下次可能会错过更合适的设计(这段话来自《Pro javaScript Design Patterns》)。
回到正题,前面的单个例子还是有缺点的。它只能用于创建遮罩层。如果我需要编写一个函数来创建一个唯一的xhr对象呢?你能找到一个通用的单例包装器吗?
js函数是第一种类型,这意味着该函数也可以作为参数传递。看看最后的代码。
var singleton=函数(fn ){var结果;返回函数(