JavaScript设计模式之单例模式实例


Posted in Javascript onSeptember 24, 2014

《Practical Common Lisp》的作者 Peter Seibel 曾说,如果你需要一种模式,那一定是哪里出了问题。他所说的问题是指因为语言的天生缺陷,不得不去寻求和总结一种通用的解决方案。

不管是弱类型或强类型,静态或动态语言,命令式或说明式语言、每种语言都有天生的优缺点。一个牙买加运动员, 在短跑甚至拳击方面有一些优势,在练瑜伽上就欠缺一些。

术士和暗影牧师很容易成为一个出色的辅助,而一个背着梅肯满地图飞的敌法就会略显尴尬。 换到程序中, 静态语言里可能需要花很多功夫来实现装饰者,而js由于能随时往对象上面扔方法,以至于装饰者模式在js里成了鸡肋。

讲 Javascript 设计模式的书还比较少,《Pro javaScript Design Patterns》是比较经典的一本,但是它里面的例子举得比较??拢??越岷衔以诠ぷ髦行垂?拇?耄?盐业睦斫庾芙嵋幌隆H绻?业睦斫獬鱿至似?睿?氩涣咧刚??/p>

一、单例模式

单例模式的定义是产生一个类的唯一实例,但js本身是一种“无类”语言。很多讲js设计模式的文章把{}当成一个单例来使用也勉强说得通。因为js生成对象的方式有很多种,我们来看下另一种更有意义的单例。

有这样一个常见的需求,点击某个按钮的时候需要在页面弹出一个遮罩层。比如web.qq.com点击登录的时候.

JavaScript设计模式之单例模式实例

这个生成灰色背景遮罩层的代码是很好写的.

var createMask = function(){

return document,body.appendChild(  document.createElement(div)  );

}

$( ‘button' ).click( function(){

Var mask  = createMask();

mask.show();

})

问题是, 这个遮罩层是全局唯一的, 那么每次调用createMask都会创建一个新的div, 虽然可以在隐藏遮罩层的把它remove掉. 但显然这样做不合理.

再看下第二种方案, 在页面的一开始就创建好这个div. 然后用一个变量引用它.

var mask = document.body.appendChild( document.createElement( ”div' ) );

$( ”button' ).click( function(){

mask.show();

} )

这样确实在页面只会创建一个遮罩层div, 但是另外一个问题随之而来, 也许我们永远都不需要这个遮罩层, 那又浪费掉一个div, 对dom节点的任何操作都应该非常吝啬.

如果可以借助一个变量. 来判断是否已经创建过div呢?

var mask;

var createMask = function(){

if ( mask ) return mask;

else{

mask = document,body.appendChild(  document.createElement(div)  );

return mask;

}

}

看起来不错, 到这里的确完成了一个产生单列对象的函数. 我们再仔细看这段代码有什么不妥.

首先这个函数是存在一定副作用的, 函数体内改变了外界变量mask的引用, 在多人协作的项目中, createMask是个不安全的函数. 另一方面, mask这个全局变量并不是非需不可. 再来改进一下.

var createMask = function(){

var mask;

return function(){

return mask || ( mask = document.body.appendChild( document.createElement(‘div') ) )

}

}()

用了个简单的闭包把变量mask包起来, 至少对于createMask函数来讲, 它是封闭的.

可能看到这里, 会觉得单例模式也太简单了. 的确一些设计模式都是非常简单的, 即使从没关注过设计模式的概念, 在平时的代码中也不知不觉用到了一些设计模式. 就像多年前我明白老汉推车是什么回事的时候也想过尼玛原来这就是老汉推车.

GOF里的23种设计模式, 也是在软件开发中早就存在并反复使用的模式. 如果程序员没有明确意识到他使用过某些模式, 那么下次他也许会错过更合适的设计 (这段话来自《松本行弘的程序世界》).

再回来正题, 前面那个单例还是有缺点. 它只能用于创建遮罩层. 假如我又需要写一个函数, 用来创建一个唯一的xhr对象呢? 能不能找到一个通用的singleton包装器.

js中函数是第一型, 意味着函数也可以当参数传递. 看看最终的代码.

var singleton = function( fn ){

var result;

return function(){

return result || ( result = fn .apply( this, arguments ) );

}

}

var createMask = singleton( function(){

return document.body.appendChild( document.createElement(‘div') );

})

用一个变量来保存第一次的返回值, 如果它已经被赋值过, 那么在以后的调用中优先返回该变量. 而真正创建遮罩层的代码是通过回调函数的方式传人到singleton包装器中的. 这种方式其实叫桥接模式. 关于桥接模式, 放在后面一点点来说.

然而singleton函数也不是完美的, 它始终还是需要一个变量result来寄存div的引用. 遗憾的是js的函数式特性还不足以完全的消除声明和语句.

Javascript 相关文章推荐
网页开发中的容易忽略的问题 javascript HTML中的table
Apr 15 Javascript
JavaScript浏览器选项卡效果
Aug 25 Javascript
js+css使DIV始终居于屏幕中间 左下 左上 右上 右下的代码集合
Mar 10 Javascript
javascript 二进制运算技巧解析
Nov 27 Javascript
js this函数调用无需再次抓获id,name或标签名
Mar 03 Javascript
js 判断浏览器使用的语言示例代码
Mar 22 Javascript
浅谈Javascript Base64 加密解密
Dec 28 Javascript
HTML5+setCutomValidity()函数验证表单实例分享
Apr 24 Javascript
JS实现简单的右下角弹出提示窗口完整实例
Jun 21 Javascript
jquery dataTable 后台加载数据并分页实例代码
Jun 07 jQuery
详解小程序原生使用ES7 async/await语法
Aug 06 Javascript
Vue Element plus使用方法梳理
Dec 24 Vue.js
JavaScript中实现异步编程模式的4种方法
Sep 24 #Javascript
JavaScript设计模式之观察者模式(发布者-订阅者模式)
Sep 24 #Javascript
JavaScript获取图片真实大小代码实例
Sep 24 #Javascript
再探JavaScript作用域
Sep 24 #Javascript
深入理解javascript原型链和继承
Sep 23 #Javascript
深入理解javascript构造函数和原型对象
Sep 23 #Javascript
常用的jquery模板插件——jQuery Boilerplate介绍
Sep 23 #Javascript
You might like
php 随机数的产生、页面跳转、件读写、文件重命名、switch语句
2009/08/07 PHP
关于mysql字符集设置了character_set_client=binary 在gbk情况下会出现表描述是乱码的情况
2013/01/06 PHP
C#使用PHP服务端的Web Service通信实例
2014/04/08 PHP
php实现的通用图片处理类
2015/03/24 PHP
php源码 fsockopen获取网页内容实例详解
2016/09/24 PHP
PHP读MYSQL中文乱码的快速解决方法
2016/10/01 PHP
Laravel框架基于中间件实现禁止未登录用户访问页面功能示例
2019/01/17 PHP
优秀js开源框架-jQuery使用手册(1)
2007/03/10 Javascript
javascript中[]和{}对象使用介绍
2013/03/20 Javascript
jquery 中ajax执行的优先级
2015/06/22 Javascript
JavaScript基础教程——入门必看篇
2016/05/20 Javascript
js防阻塞加载的实现方法
2016/09/09 Javascript
js日期相关函数dateAdd,dateDiff,dateFormat等介绍
2016/09/24 Javascript
AngularJS中的缓存使用
2017/01/11 Javascript
基于jQuery实现数字滚动效果
2017/01/16 Javascript
Node.js微信 access_token ( jsapi_ticket ) 存取与刷新的示例
2017/09/30 Javascript
浅谈ajax请求不同页面的微信JSSDK问题
2018/02/26 Javascript
vue-cli 3.0 自定义vue.config.js文件,多页构建的方法
2018/09/19 Javascript
vue组件定义,全局、局部组件,配合模板及动态组件功能示例
2019/03/19 Javascript
ios中视频的最后一桢问题解决
2019/05/14 Javascript
TypeScript之调用栈的实现
2019/12/31 Javascript
vue实现户籍管理系统
2020/05/29 Javascript
Vue使用screenfull实现全屏效果
2020/09/17 Javascript
详解Python验证码识别
2016/01/25 Python
python对json的相关操作实例详解
2017/01/04 Python
pytorch使用指定GPU训练的实例
2019/08/19 Python
scikit-learn线性回归,多元回归,多项式回归的实现
2019/08/29 Python
非常漂亮的CSS3百叶窗焦点图动画
2016/02/24 HTML / CSS
机电一体化求职信
2014/03/10 职场文书
法律系毕业生自荐信范文
2014/03/27 职场文书
消防宣传口号
2014/06/16 职场文书
2014镇党委班子对照检查材料思想汇报
2014/09/23 职场文书
再婚婚前财产协议书范本
2014/10/19 职场文书
婚宴祝酒词大全
2015/08/10 职场文书
2016年“我们的节日·中秋节”活动总结
2016/04/05 职场文书
浅谈Redis位图(Bitmap)及Redis二进制中的问题
2021/07/15 Redis