万事开头难,作为正经历菜鸟赛季的前端player,已经忘记第一次告诉自己要写一些东西出来是多久以的事情了。。。如果,你也和我一样,那就像我一样,从现在开始,从看到这篇文章开始,打开电脑,敲下你的第一篇文章(或者任何形式的文字)吧。
首先,respect一下:以下文章内容全部来自最近在看的一本书《javascript设计模式与开发实践》,作者曾探。感谢作者的辛苦付出,以下内容算是个人的一个读书笔记,如果有理解有误或不合适的内容,欢迎随时联系更改或删除。
其次,欢迎各路大神diss。
最后,接下来正式开始我的flow。
一、什么是设计模式
相信任何一个从事和代码相关工作的人都或多或少地听说过“设计模式”这个名词,每个人也都有自己的理解。从我个人来看,设计模式就是“套路”,知道了这个套路的话,你就多了一些处理问题的技能(或者是更优雅地解决);那么,如果不知道这个套路也并不能说明什么,只是你还没总结或者你正在使用却不自知而已。即使是真的不知道,也没关系,就从这篇文章开始吧。
设计模式的思想来源于建筑行业,建筑学的研究人员花了长达20年的时间总结了在建筑中为解决同样的问题而设计的不同的建筑结构,从中找到一些高质量的相似的通用结构,称之为“模式”,软件工程大神们受到启发,进而总结了23种常用的软件开发设计模式,录入到《设计模式:可复用面向对象软件的基础》
设计模式的定义是:在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案(并不必须是面向对象)。通俗来说,设计模式就是给我们软件开发过程中经常用的一些套路起了一个名字,就变成了一个看起来很高大上的东西。
建筑会因为不同的风格去对一些相同的结构做一些不一样的设计,对软件来说也一样,不同的语言,设计模式的实现也是不一样的。google的某一位大神曾经在文章中指出23种设计模式有16种已经在lisp中默认实现了。比如,命令模式,在java中需要一个命令类,一个接受者类,一个调用者类,并把命令对象在接受者类中四处传递。但在lisp或javascript这种将函数作为一等公民的语言中,函数本身就可以在对象中互相传递,因此,命令模式在这两种语言中就成为了一种隐性模式。
智者说,凡是都有度(别问我是哪个智者)。对于鼓吹设计模式和贬低设计模式的,我只能说,你牛你有理。个人认为应该没有放之四海而皆准的东西(貌似是个悖论,尴尬)。如果有,那就有吧,反正不是设计模式。为什么说设计模式简洁而优雅的解决方案,是因为从软件开发的角度来看,健壮和易扩展是非常非常重要的衡量指标,而设计模式恰恰帮助我们更好地解决了这个问题。但为什么说“度”呢,因为设计模式也并不适合用在任何开发的过程中。如果在一个简单的小项目中,明明一个函数就可以完成的功能,非要为了炫技或其他什么原因,增加额外的许多代码实现一个模式,项目代码增加许多,自然增加了bug的几率。另外,明明是一次使用的东西,偏偏要做过度的设计去用模式做扩展设计,这自然也是没有必要的。
二、设计模式之单例模式
单例模式应该是设计模式中最简单的一个模式,在许多书中都是作为第一个来讲。单例,顾名思义,保证一个类只有一个实例,并在项目代码中可以全局被访问。
在面向对象的语言中,比如,c++、java等,单例模式通常是定义类时将构造函数设为private,保证对象不能在外部被new出来,同时给类定义一个静态的方法,用来获取或者创建这个唯一的实例。javascript同样可以模仿这个过程来实现单例,代码如下:
var Singleton = function (name) {
this.name = name;
this.instance = null;
}
Singleton.getInstance = function(name) {
if (!this.instance) {
this.instance = new Singleton(name);
}
return this.instance;
}
很显然,我们在代码中需要用到Singleton的时候,只需要Singleton.getInstance(‘PGOne’)即可获得唯一的实例。但是却有一些缺陷:当我们new Singleton(‘Gai’)的时候,仍可以new出实例,另外,Singleton做了一些和自己无关的事情,于是乎就有了另一种实现。
var Singleton = function (name) {
this.name = name;
this.instance = null;
}
var ProxySingleton = (function () {
var instance;
return function (name) {
if (!instance) {
instance = new Singleton(name);
}
return instance;
}
})();
在这种实现中,我们可以通过new ProxySingleton(‘PGOne’)获取Singleton的实例,单例控制就分离到了代理类中,保证了Singleton的纯洁。甚至可以把Singleton使用闭包封装成私有变量,彻底阻止直接的new调用。
上面这些是在JavaScript中模仿静态语言c++或java中单例模式的实现,但是,在JavaScript中,看起来似乎有些奇怪和多余。因为,JavaScript作为一种无类(class-freee)语言,生搬过来的单例模式并没有太多意义。在JavaScript中,常见对象非常简单,如果需要一个单例,我们只需要声明一个字面量的对象,作为全局变量就可以了,何必声明一个构造函数,再去new出来呢?虽然全局变量不是单例模式,但是在JavaScript中,我们却常常把全局变量当做单例模式来用,因为它确实能完成单例模式的功能。但是全局变量最大的问题就是污染全局空间,以至于一直以来,滥用全局变量都被视为糟糕的代码。为了尽可能减少全局变量的影响,在JavaScript中,命名空间和闭包封装私有变量成了惯用的手段。那么在JavaScript中,单例到底应该是什么样子呢?JavaScript终极单例如下:
var Singleton = function (fn) {
var result;
return function () {
return result || (result = fn.apply(this, arguments));
}
}
var createSingleDialog = function() {
var div = document.createElement('div');
div.innerHTML = '我是弹窗';
div.style.display = 'none';
document.body.appendChild('div');
return div;
}
// 使用
var getDialog = Singleton(createSingleDialog);
document.getElementById('btn').onclick = function () {
var dialog = getDialog();
dialog.style.display = 'block';
}
在上面代码中,Singleton是一个通用的代理管理器,可以通过传入不同的功能函数进而返回一个单例获取器。在上面代码中,是获取一个弹窗的单例,我们同样可以通过传入一个导航栏生成函数,来获取一个导航栏的单例。另外,上面代码中,单例只在需要的时候才会被创建,这也是单例模式所要求的,然而,通过全局对象和闭包实现的单例,确实在开始就创建了实例。
需要说明的是,由于javascript的应用场景主要在浏览器,通过dom操作也同样可以做到单例,所以new这样的单例看起来使用场景并不大,但是,上面的一小段代码远不止单例那么简答。在实际的开发中,我们经常会遇到这样的问题,在异步获取数据之后或是弹窗出来的时候,需要给一部分的dom元素绑定事件。比如说,有一个负责数据展示和操作的弹窗,每次弹窗显示的时候都需要将新的数据绑定到弹窗的dom元素上,这时就会遇到dom重复绑定事件的问题,上面代码就可以完美解决这个问题。
var bindEvent = getSingle(function () {
document.getElement('btn').onClick = function(){
alert('new data');
}
return true;
});
var showDialog = function () {
console.log('显示弹窗');
bindEvent();
}
showDialog();
showDialog();
第一次写东西,感觉想要把一个东西写清楚还真是一件复杂的事情。
许久不见朋友突然的一个约饭,帮朋友搬一天的家,诸多借口,又差点掐死了这篇内容。还好,我今天不困。复制粘贴一番,勉强写出来一点。
设计模式,未完待续。。。。
番外:
to be real很酷,但请不要为了real而real。人人都向往自由,但世界却不能没了规则。客套,请不要假装不懂。
—-中国有嘻哈观感,我站万磁王!