JS 单例模式
《parctical common lisp》的作者曾说,如果你需要一种模式,那一定是哪里出了问题。他所说的问题是指因为语言的天生缺陷,不得不去寻求和总结一种通用的解决方案。
不管是弱类型或强类型,静态或动态语言,命令式或说明式语言、每种语言都有天生的优缺点。一个牙买加运动员, 在短跑甚至拳击方面有一些优势,在练瑜伽上就欠缺一些。
术士和暗影牧师很容易成为一个出色的辅助,而一个背着梅肯满地图飞的敌法就会略显尴尬。 换到程序中, 静态语言里可能需要花很多功夫来实现装饰者,而js由于能随时往对象上面扔方法,以至于装饰者模式在js里成了鸡肋。
讲javascript设计模式的书还比较少. Pro javaScript Design Patterns.是比较经典的一本,但是它里面的例子举得比较啰嗦,所以结合我在工作中写过的代码,把我的理解总结一下。如果我的理解出现了偏差,请不吝指正。
一 单例模式
单例模式的定义是产生一个类的唯一实例,但js本身是一种“无类”语言。很多讲js设计模式的文章把{}当成一个单例来使用也勉强说得通。因为js生成对象的方式有很多种,我们来看下另一种更有意义的单例。
有这样一个常见的需求,点击某个按钮的时候需要在页面弹出一个遮罩层。比如web.qq.com点击登录的时候.
这个生成灰色背景遮罩层的代码是很好写的.
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的函数式特性还不足以完全的消除声明和语句.
JS 单例模式的更多相关文章
- 浅谈js单例模式
单例模式就是在系统中保存一个实例,就是一个全局变量,在团队开发中,为了实现一些相似的功能,比如不同页面之间的表单验证,可能需求是不一样的,但是呢命名可能一样,这时就会产生冲突,这时候单例模式就能很好的 ...
- js单例模式
js实现单例模式,经常使用两种方法,一种是使用构造函数的静态属性中缓存该实例,另一种是将实例包装在闭包中. 第一种实现方式: //静态属性中单例模式 function Universe() { if ...
- JS单例模式在工作中的使用
为了尽可能的减少全局变量的污染,在写js的时候可以采用单例模式,形式如下: 比如有一个js叫demo.js,那么我们可以在js里这样写: var demo = {} 这样做的目的是将整个js当成一个对 ...
- js单例模式详解实例
这篇文章主要介绍了什么是单例单例模式.使用场景,提供了3个示例给大家参考 什么是单例? 单例要求一个类有且只有一个实例,提供一个全局的访问点.因此它要绕过常规的控制器,使其只能有一个实例,供使用者使用 ...
- js 单例模式笔记
单例指一个类只有一个实例,这个类自行创建这个实例. 利用对象字面量直接生成一个单例: var singleton = { prop: 1, method: function(){ console.lo ...
- js 单例模式的实现方式----闭包和构造函数内部判断
闭包: var singleton = function( fn ){ var result; return function(){ return result || ( result = fn .a ...
- js原生设计模式——8单例模式之简约版属性样式方法库
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8&qu ...
- 收藏的js学习小例子
1.js模拟java里的Map function Map(){ var obj = {} ; this.put = function(key , value){ obj[key] = value ; ...
- JS学习笔记3_函数表达式
1.函数表达式与函数声明的区别 函数声明有“提升”(hoisting)的特性,而函数表达式没有.也就是说,函数声明会在加载代码时被预先加载到context中,而函数表达式只有在执行表达式语句时才会被加 ...
随机推荐
- python学习(二十四) 字符串格式化
1: Test 1 a = 'city' b = 'country' print(" aaa %s bbb %s " % (a, b)) result: aaa city bbb ...
- pubmed检索完全攻略
第一章 进入PubMed魔法学校--PubMed 概述 有位退休的老教授不止一次的向我感叹:"你们现在真是幸福,我们那时候要查一篇相关的文献,要到图书馆一本一本目录去检索.尤其是做一些别人不 ...
- 解决oracle11g无法导出空表问题
11G中有个新特性,当表无数据时,不分配segment,以节省空间 解决方法: 1.insert一行,再rollback就产生segment了. 该方法是在在空表中插入数据,再删除,则产生segmen ...
- Rhythmk 一步一步学 JAVA(7): jsp 自定义标签
1.实现Tag接口: TagSupport类实现了Tag接口,为我们提供了4个重要的方法(见表6-5). 1.1. TagSupport类中的常用方法 int doStartTag ...
- 「小程序JAVA实战」小程序模块之间引用(19)
转自:https://idig8.com/2018/08/09/xiaochengxu-chuji-19/ 上一节,讲了页面引用模块的概念,如果是模块之前引用呢?源码:https://github.c ...
- 转载----我与CMDB不得不说的故事
每次读到配置管理相关的书籍时,我总在想:“这些定义很精准,流程也很完整,但这不是真正的难题.”对于一个配置管理者来说,真正的难题不是绘制“庞大而精美”的数据模型,不是设计“全天候.无死角”的管控流程, ...
- Variant
class RTL_DELPHIRETURN Variant: public TVarData Variant转换为字符串 System::Variants::VarToStr VariantArra ...
- 集群的session问题解决方案
一.nginx ip_hash 同一个ip的请求转发到同一个服务器,太low不解释. 二.spring-session 原理:存入session中的key-value,同时存放到redis中,如果se ...
- Sersync + Rsync 代码分发
简介: Sersync 是基于 inotify 来编写的 Linux 系统文件监控工具,当监控到文件发生变化时,调用 rsync 同步文件. 类似的功能,以前有用 rsync + inotify 实现 ...
- Squid 代理服务器日志管理
简介: Squid 服务器日志增长是很快的,如果不做处理的话,可以会由于系统限制单文件大小,而导致 Squid 服务停止,太大的日志文件也不适合分析. 一.日志配置 shell > grep ' ...