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 相关文章推荐
Auntion-TableSort国人写的一个javascript表格排序的东西
Nov 12 Javascript
js自动闭合html标签(自动补全html标记)
Oct 04 Javascript
jquery滚动组件(vticker.js)实现页面动态数据的滚动效果
Jul 03 Javascript
JavaScript实现的一个计算数字步数的算法分享
Dec 06 Javascript
JavaScript中eval()函数用法详解
Dec 14 Javascript
微信小程序 自己制作小组件实例详解
Dec 22 Javascript
Vue系列:通过vue-router如何传递参数示例
Jan 16 Javascript
js实现城市级联菜单的2种方法
Jun 23 Javascript
jQuery自动或手动图片切换效果
Oct 11 jQuery
javascript 判断用户有没有操作页面
Oct 17 Javascript
angularJS实现不同视图同步刷新详解
Oct 09 Javascript
详解Vue demo实现商品列表的展示
May 07 Javascript
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 AJAX实例根据邮编自动完成地址信息
2008/11/23 PHP
Yii实现微信公众号场景二维码的方法实例
2020/08/30 PHP
javascript 表单规则集合对象
2009/07/21 Javascript
国外大牛IE版本检测!现在IE都到9了,IE检测代码
2012/01/04 Javascript
设为首页加入收藏兼容360/火狐/谷歌/IE等主流浏览器的代码
2013/03/26 Javascript
js操作checkbox遇到的问题解决
2013/06/29 Javascript
open 动态修改img的onclick事件示例代码
2013/11/13 Javascript
JS实现网页滚动条感应鼠标变色的方法
2015/02/26 Javascript
详谈Ajax请求中的async:false/true的作用(ajax 在外部调用问题)
2017/02/10 Javascript
js获取ip和地区
2017/03/10 Javascript
JS对象与JSON互转换、New Function()、 forEach()、DOM事件流等js开发基础小结
2017/08/10 Javascript
解决微信二次分享不显示摘要和图片的问题
2017/08/18 Javascript
详解使用React进行组件库开发
2018/02/06 Javascript
浅谈AngularJS中$http服务的简单用法
2018/05/15 Javascript
详解vue-cli中模拟数据的两种方法
2018/07/03 Javascript
手挽手带你学React之React-router4.x的使用
2019/02/14 Javascript
解决 viewer.js 动态更新图片导致无法预览的问题
2019/05/14 Javascript
Vue实现导航栏点击当前标签变色功能
2020/08/19 Javascript
Vue CL3 配置路径别名详解
2019/05/30 Javascript
[07:54]DOTA2 MV《我的动力鞋》 ImbaTV 出品
2014/11/21 DOTA
[00:59]DOTA2荣耀之路1:Doom is back!weapon X!
2018/05/22 DOTA
[49:30]DOTA2-DPC中国联赛正赛 Dragon vs Dynasty BO3 第二场 3月4日
2021/03/11 DOTA
详解设计模式中的工厂方法模式在Python程序中的运用
2016/03/02 Python
python基于pyDes库实现des加密的方法
2017/04/29 Python
对python中Matplotlib的坐标轴的坐标区间的设定实例讲解
2018/05/25 Python
Python基于SMTP协议实现发送邮件功能详解
2018/08/14 Python
python变量赋值方法(可变与不可变)
2019/01/12 Python
Python----数据预处理代码实例
2019/03/20 Python
Django框架文件上传与自定义图片上传路径、上传文件名操作分析
2019/05/10 Python
python将四元数变换为旋转矩阵的实例
2019/12/04 Python
Python urlencode和unquote函数使用实例解析
2020/03/31 Python
pytorch cuda上tensor的定义 以及减少cpu的操作详解
2020/06/23 Python
鼠标滚轮事件和Mac触控板双指事件
2019/12/23 HTML / CSS
Java 中访问数据库的步骤?Statement 和PreparedStatement 之间的区别?
2012/06/05 面试题
党员批评与自我批评材料
2014/10/14 职场文书
教师病假条范文
2015/08/17 职场文书