这是关于JavaScript设计模式系列文章的第一篇。1995年,Erich Gamma, Richard Helm, Ralph Johnson和John Vlissides(有名的四人组,译注:The Gang of Four, 后文统称GoF)出版了《设计模式:可复用面向对象软件的基础》,一本为软件架构和设计难题提供一系列解决方案的书。书中同时为这些解决方案(译注:即各类设计模式)提供了一份词汇参照。若是你感兴趣能够在维基百科上了解更多。
那本书里对于每种解决方案的实现是用C++和Smalltalk写的,它们与JavaScript截然不同。另外一本书《Pro JavaScript Design Patterns》则是用JavaScript来实现那些设计模式的。我本但愿从这本书里能获得不少知识,然而事与愿违……他们可能只是想调起你的胃口让你去买这本书罢了。若是你真的买了这本书必定要让他们知道是我推荐你买的。或许他们会给我一些补偿(也可能不会,可是至少我是这么但愿的)。javascript
在JavaScript里,单例模式(the Singleton Pattern)很是简单,简单到能够被忽略,可是它在技术层面是如何工做的,咱们仍是有必要了解一下的。单例的代码写在一个单独的对象里,所以你不须要去实例化一个新对象就能够在任何你须要的时候使用它的资源,单例容许在全局范围内访问它的资源。
在JavaScript里,咱们常在管理命名空间时使用单例模式,它能够下降你在代码中建立全局变量的数量。单例模式在JavaScript里要比在其余语言中更有用,由于它能够用命名空间来下降全局变量所带来的风险。php
这是一个最基本最简单的用JavaScript实现的单例模式。它就是一个简单的有一些方法和属性的对象字面量,假想它们是由于某种关系才被放到一块儿。java
var Singleton = { attr: 1, another_attr: 'value', method: function() {...}, another_method: function() {...} };
由于它是一个对象字面量,因此我不须要实例化它,而且这个对象仅此一个。也就是说咱们能够经过一个全局变量的入口访问全部的方法和属性,以下所示:ajax
Singleton.attr += 1;
Singleton.method();
...
单例模式在JavaScript中的一种使用情形就建立命名空间。像Java和C#这样的语言,命名空间这种特性已经被内建到语言自己了,而且命名空间在这些语言中是必须的。经过建立命名空间或者包来把代码组织到逻辑块中。这也是在JavaScript中使用单例模式最重要的缘由,当你经过使用命名空间把你的代码从全局做用域移动到一个新的单例中的时候,能够避免全局变量被意外覆盖(overwrite)以及全局变量引发其余的bug。
使用单例模式管理命名空间很是简单。一样你能够仅仅经过建立一个对象字面量就搞定:设计模式
var Namespace = { Util: { util_method1: function() {...}, util_method2: function() {...} }, Ajax: { ajax_method: function() {...} }, some_method: function() {...} };
如你所见,如今若是你想使用一个实用方法,能够在Namespace.Util
下面找到它,就像下面代码中所示的那样。固然,下面所示的some_method
方法并无在单例中嵌套多层。闭包
Namespace.Util.util_method1();
Namespace.Ajax.ajax_method();
Namespace.some_method();
一般你有可能会把这些方法做为全局函数,这也就意味着它们很是有可能会被覆盖,尤为是像get
这样简单的名字,诸如此类的名字再日常不过了。你只须要把所有的变量和函数添加到一个单例中,就能够彻底不给别人篡改你代码的机会。架构
在不少状况下,一个网站的某些页面运行的JavaScript代码和其余页面是不一样的。你能够用单例管理命名空间的技术来为每一个页面组织专门的代码,而后在页面加载完成后执行它们。ide
Namespace.homepage = { init: function() {...}, method1: function() {...}, method2: function() {...} } Namespace.contactpage = { init: function() {...}, method1: function() {...}, method2: function() {...} } Namespace.pageutil = { getPageName: function() { // 返回当前页面的标识符 } } var pageName = Namespace.pageutil.getPageName(); window.onload = Namespace[pageName].init;
这对于针对不一样页面上出现的不一样表单添加验证代码很是有用。你甚至能够提取出一个新的命名空间来封装些实用功能,好比我这里的Namespace.pageutil.getPageName
。这和我以前提到的稍有不一样,由于getPageName
方法并不是某个页面专有的代码,可是却能够用它来正确映射到页面专有代码。函数
《Pro JavaScript Design Patterns》这本书中对单例模式介绍了更多,然而事实上我把书中整整6页的内容压缩成这篇短小的博客,书中也说起了经过闭包,被动初始化和分支建立私有变量。就像我在文章开头所说,关于这本书我不想复制太多的内容过来,只是想勾起你的兴趣让你去买它。这么作的好处是,你既能够给他们经济援助,同时也能避免我让人家给告了。更况且,一篇博客文章不该该硬塞下一本书中一章的内容。网站
若是你认为本文对你有所帮助或者你只是纯粹地喜欢这篇文章,请用文章下方的社交分享按钮把它传播出去。我这种从后山来的历来不敢想像能有人像你这样帮我。谢谢!
请继续关注JavaScript设计模式系列的更多文章:
– 单例模式(Singleton Pattern)
– 桥接模式(Bridge Pattern)
– 组合模式(Composite Pattern)
– 外观模式(Facade Pattern)
– 适配器模式(Adapter Pattern)
– 装饰者模式(Decorator Pattern)
– 工厂模式(一)(Factory Pattern Part 1)
– 工厂模式(二)(Factory Pattern Part 2)
– 代理模式(Proxy Pattern)
– 观察者模式(Observer Pattern)
– 命令模式(Command Pattern)
– 责任链模式(Chain of Responsibility Pattern)
转载请注明:
英文做者:Joe Zim
英文原文:http://www.joezimjs.com/javascript/javascript-design-patterns-singleton/
中文译者:David @CodingSerf
中文译文:http://www.codingserf.com/index.php/2015/05/javascript-design-patterns-singleton/