singleton模式,又名单例模式。顾名思义,就是只能实例化一次的类(javascript中没有真正的类,我们通常用函数来模拟类,习惯称之为"伪类")。具体地说,singleton模式,就是在该实例不存在的情况下,可以通过可以方法创建一个类来实现创建类的新实例;如果实例已经存在,它会返回一个该对象的引用。

  接下来我将用一个案列来将singleton模式进行分析。在我们的web网上通常会看到一些图片的放大查看,如下图:

                                                                   

  当我的鼠标碰到图中的帅哥时,它会出现一个放大的图标(这里放大图标有点奇怪= = !),于是我们点击后会出现这样的画面如下图:

                                      

  没错,放大后,就可以看到这个美男子的芳容了。这就是我给大家带来的例子,现在我们对所用到的js进行分析:(html,css部分忽略)

  首先我们编写兼容各版本浏览器的事件函数(参考javascript高级程序设计的事件部分):这个示例中只会用到EventUtil中的adds函数。

    

var EventUtil = {
adds : function(element,type,func){
if(element.addEventListener){
element.addEventListener(type,func,false);
}else if(element.attachEvent){
element.attachEvent("on"+type,func);
}else{
element["on"+type] = func;
}
},
removes : function(element,type,func){
if(element.removeEventListener){
element.removeEventListener(type,func,false);
}else if(element.detachEvent){
element.detachEvent("on"+type,func);
}else{
element["on"+type] = null;
}
},
getEvent : function(event){
return event ? event : window.event;
},
getTarget : function(event){
return event.target || event.srcElement;
},
preventDefault : function(event){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue = false;
}
},
stopPropagation : function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble = true;
}
}
} function $(id){
return typeof id === "string" ? document.getElementById(id) : id;
}

  接下来开始设计这个放大的功能。首先你会想到既然我要放大,那我肯定要有放大的图片以及放大图片所盛放的容器。没错,我们创建这些必定会用到一系列的Dom方法,这个稍后会见到。

  当我们每次放大图片的时候有一个地方是不会变的,就是背景阴影和盛放图片的容器。每次都是一个背景的div和一个盛放图片的div对象在我们的浏览器中显示,然后关闭后显示。具体分析就是这个对象可以是唯一的,我只需要将它保存在缓存中,放大的时候将这个对象通过document.body.appendChild()方法将它载入到我们的页面,关闭的时候再使用removeChild()方法将它从页面移除(并不是从缓存移除),那我们如何保证每次调用这个对象时都是第一次调用时的对象呢?(这里说的可能有点复杂,简洁说就是每次都是调用最初的那个对象)。

  这个时候singleton模式就可以很好的解决这个问题。回到刚刚所有的保存在缓存中,我们都很熟悉js等高级语言的垃圾回收机制,它采用引用计数的方式(参考javascript高级程序设计第四章)来进行内存管理。既然我们需要这个对象始终保持唯一,那么就需要将这个对象始终保存在缓存,也就是说不然它被回收,那我们应该怎么做到呢?

  闭包。没错,我们可以采用闭包(singleton模式的建立依赖闭包,这个概念就不提了)。于是我们可以采取如下的方法实现:

var showBig = (function(){
var listen; //作为对象的引用
function initial(){
var listen_2,listen_3;
function createBgShadow(){
//先不考虑这里的代码
}
function createBgCenterDiv(){
//先不考虑这里的代码
}
return{
create : function(){
//先不考虑这里的代码
},
removes : function(){
//先不考虑这里的代码
}
}
}
return{
getShadow : function(){
if(!listen){ //如果listen没有引用这个对象,那么就调用一次initial方法。若引用了,则直接跳过
listen = initial();
}
return listen; //返回这个对象(或者对象的引用)
},
getPic : function(picUrl){
//先不考虑这里的代码
}
}
})();

  上述代码中可以看到,为了保存我要保留在内存中的对象,我使用了一个Listen作为我的监听器(我个人称之为监听器,本意为这个对象的引用)。因为Listen在闭包环境中,当第一次返回Initial()方法后,listen始终带有initial()方法所返回的对象,当第二次或者以后再次调用时,listen并未从内存中消失,因此直接返回内存中的listen即可。

  而我们要返回的是一个背景阴影对象,就可以这么做:

  

function createBgShadow(){
//阴影背景的创建
var shadowBg = document.createElement("div");
shadowBg.setAttribute("id","picShadow");
return shadowBg;
}
function createBgCenterDiv(){
//盛放图片的容器的创建
var shadowBgCenterDiv = document.createElement("div");
shadowBgCenterDiv.setAttribute("id","realDiv");
//放大之后的图片的创建
var shadowBgCenterPic = document.createElement("img");
shadowBgCenterPic.setAttribute("src","");
shadowBgCenterPic.setAttribute("id","realPic");
shadowBgCenterDiv.appendChild(shadowBgCenterPic);
//取消放大的图片(那个×)的创建
var cancel = document.createElement("img");
cancel.setAttribute("id","cancelPic");
cancel.setAttribute("src","img/cancel.png");
shadowBgCenterDiv.appendChild(cancel);
return shadowBgCenterDiv;
}
//注意它们都返回了一个DOM的节点对象。这一步很关键。

  然后将这两个对象保存在Listen_2,listen_3中,同样他们也要和initial()一样,保存在内存中。于是我们可以这么做:

return{
//create()方法将节点加入到html页面
create : function(){
if(!listen_2 || !listen_3){
//listen2保存阴影背景的节点
listen_2 = createBgShadow();
//listen3保存图片以及图片容器的节点
listen_3 = createBgCenterDiv();
}
//由于listen_2和Listen_3也使用闭包,始终保存在内存中,这里也只会创建一次对象
document.body.appendChild(listen_2);
document.body.appendChild(listen_3);
},
//removes()方法将节点移出html页面
removes : function(){
var picShadow = $("picShadow");
var realDiv = $("realDiv");
document.body.removeChild(picShadow);
document.body.removeChild(realDiv);
}
}

  这时候已经很明确了singleton模式的作用,它避免了多次创建重复的对象,将我们可以反复使用的对象只创建一次。我们用listen,listen_2,listen_3来保存了这些我们需要反复使用的对象。singleton模式利用闭包可以更快速的创建我们需要的应用,但是这些对象也会一直保存在我们的内存中,所以singleton模式是有它自己的适用范围的,重点突出一个"单"字。

  简单的总结一下singleton模式:

  1.singleton模式是javascript最基本,最有用的模式之一,它提供了一种将代码组织为一个逻辑单元的手段,这个逻辑单元中的代码通过单一的变量进行访问。(通过单一的接口进行访问,这里是第一个单例中的create()接口和第二个单例中的getShadow()接口)

  2.singleton模式可以用来划分命名空间,以减少全局变量的泛滥。(上述我们只是用了一个showBig的全局变量)

  3.singleton模式通过js的闭包特性反复引用内存中的同一对象,使运用程序更快速的执行。

  4.singleton模式的弊端在于提供的是一种单点访问,可能导致模块间的强耦合(我们的程序遵守高内聚低耦合的原则)。在选择这种模式的时候应该考虑到这种情况。并不是所有情况都要选择它,而是先分析好。

  完整代码如下(测试时不要忘了修改图片的url):

<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<style type="text/css">
*{margin:0px;padding:0px;}
div.singleton{width:177px;height:110px;margin:100px auto;position:relative;}
img{width:177px;height:110px;}
img.real{display:block;}
img.bigger{position:absolute;top:0px;left:0px;opacity:0}
img.bigger:hover{opacity:1;cursor:pointer;z-index:100;}
#picShadow{background:#555;position:fixed;top:0px;left:0px;width:100%;height:100%;filter:alpha(opacity=70);opacity:0.7;-moz-opacity:0.7;z-index:100;}
#realDiv{position:fixed;top:120px;left:283px;display:block;width:800px;height:400px;z-index:200;}
#realPic{display:block;width:auto;height:auto;max-width:780px;max-height:380px;margin:0 auto;padding:5px;background-color:#fff;}
#cancelPic{display:block;width:auto;height:auto;margin:0 auto;cursor:pointer;}
</style>
</head>
<body>
<div class="singleton">
<img class="real" id="real" src="img/cjy.jpg"/>
<img class="bigger" id="bigger" src="img/bigger.png"/>
</div>
</body>
<script type="text/javascript">
/* 兼容事件对象 */
var EventUtil = {
adds : function(element,type,func){
if(element.addEventListener){
element.addEventListener(type,func,false);
}else if(element.attachEvent){
element.attachEvent("on"+type,func);
}else{
element["on"+type] = func;
}
},
removes : function(element,type,func){
if(element.removeEventListener){
element.removeEventListener(type,func,false);
}else if(element.detachEvent){
element.detachEvent("on"+type,func);
}else{
element["on"+type] = null;
}
},
getEvent : function(event){
return event ? event : window.event;
},
getTarget : function(event){
return event.target || event.srcElement;
},
preventDefault : function(event){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue = false;
}
},
stopPropagation : function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble = true;
}
}
} function $(id){
return typeof id === "string" ? document.getElementById(id) : id;
} //单例
var showBig = (function(){
var listen;
function initial(){
var listen_2,listen_3;
function createBgShadow(){
var shadowBg = document.createElement("div");
shadowBg.setAttribute("id","picShadow");
return shadowBg;
}
function createBgCenterDiv(){
var shadowBgCenterDiv = document.createElement("div");
shadowBgCenterDiv.setAttribute("id","realDiv");
//放大图片
var shadowBgCenterPic = document.createElement("img");
shadowBgCenterPic.setAttribute("src","");
shadowBgCenterPic.setAttribute("id","realPic");
shadowBgCenterDiv.appendChild(shadowBgCenterPic);
//创建取消放大的图片
var cancel = document.createElement("img");
cancel.setAttribute("id","cancelPic");
cancel.setAttribute("src","img/cancel.png");
shadowBgCenterDiv.appendChild(cancel);
return shadowBgCenterDiv;
}
return{
create : function(){
if(!listen_2 || !listen_3){
listen_2 = createBgShadow();
listen_3 = createBgCenterDiv();
}
document.body.appendChild(listen_2);
document.body.appendChild(listen_3);
},
removes : function(){
var picShadow = $("picShadow");
var realDiv = $("realDiv");
document.body.removeChild(picShadow);
document.body.removeChild(realDiv);
}
}
}
return{
getShadow : function(){
if(!listen){
listen = initial();
alert("1");
}
return listen;
},
getPic : function(picUrl){
var realPic = $("realPic");
realPic.setAttribute("src",picUrl);
}
}
})();
//事件处理
//为方便这里不做平稳退化
var bigger = $("bigger");
var gS_1 = showBig;
EventUtil.adds(bigger,"click",function(event){
gS_1.getShadow().create();
gS_1.getPic($("real").src);
//这里做一个关闭检测
if($("cancelPic")){
var cancelPic = $("cancelPic");
EventUtil.adds(cancelPic,"click",function(event){
gS_1.getShadow().removes();
});
}
});
</script>
</html>

  

js中singleton模式解析及运用的更多相关文章

  1. js中State模式的解析及运用

     状态模式,在大的范畴中的定义为当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类.每种编程语言有不同的实现方式,运用的范围也多用于游戏之中. 这里我用javascript来模拟状 ...

  2. 转:JS中生成和解析JSON

    原文地址:JS中生成和解析JSON 1.JS中生成JSON对象的方法: var json = []; var row1 = {}; row1.id= "1"; row1.name ...

  3. js中JSON的解析(将json字符串转化为对象)和序列化(将对象转化为json字符串)(函数的功能一般都挺全的,需要的时候去查看完整函数)

    js中JSON的解析(将json字符串转化为对象)和序列化(将对象转化为json字符串)(函数的功能一般都挺全的,需要的时候去查看完整函数) 一.总结 1.JSON解析:JSON.parse(myJS ...

  4. 由项目浅谈JS中MVVM模式

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.    背景 最近项目原因使用了durandal.js和knock ...

  5. js架构设计模式——由项目浅谈JS中MVVM模式

    1.    背景 最近项目原因使用了durandal.js和knockout.js,颇有受益.决定写一个比较浅显的总结. 之前一直在用SpringMVC框架写后台,前台是用JSP+JS+标签库,算是很 ...

  6. JS中生成和解析JSON

    1.JS中生成JSON对象的方法: var json = []; var row1 = {}; row1.id= "1"; row1.name = "jyy"; ...

  7. js中严格模式

    我们在js中可以使用"use strict";定义了我们在接下来的文档输写中 将按照严格模式进行: function(){ "use strict'; ;// 在这里我们 ...

  8. JS 中Promise 模式

    异步模式在web编程中变得越来越重要,对于web主流语言Javscript来说,这种模式实现起来不是很利索,为此,许多Javascript库(比如 jQuery和Dojo)添加了一种称为promise ...

  9. (转)Spring中Singleton模式的线程安全

    不知道哪里的文章,总结性还是比较好的.但是代码凌乱,有的还没有图.如果找到原文了可以进行替换! spring中的单例 spring中管理的bean实例默认情况下是单例的[sigleton类型],就还有 ...

随机推荐

  1. jstl的formatNumber标签的四舍五入问题

    jstl的formatNumber标签的四舍五入问题 近日使用JSTL的formatNumber 标签进行四舍五入时,发现它竟然使用的是"4舍6入5奇偶"的算法. 要实现" ...

  2. Oracle学习【语句查询】

    基本查询语句any和all不能单独使用,必须和比较符一起使用>any 大于最小的例如:select * from emp where sal >any(1000,2000);<any ...

  3. Mac Zip命令

    mac终端命令 zip -[parameter] [yourName].zip someFileOrDiectory -q 表示不显示压缩进度状态 -r 表示子目录子文件全部压缩为zip //这部比较 ...

  4. OC基础-day06

    #pragma mark - Day06_01_点语法 1. 点语法. 1). 如果要访问对象的属性,还要去调用属性对应的setter getter方法.好烦躁好烦躁. 2). 点语法的作用: 快速调 ...

  5. 将数字n转换为字符串并保存到s中

    将数字n转换为字符串并保存到s中 参考 C程序设计语言 #include <stdio.h> #include <string.h> //reverse函数: 倒置字符串s中各 ...

  6. 将[{},{}]转为dict

    经常遇到一种需求,需要把从数据库取出的数据,转为dict对象([{}, {},...]-->dict). rs = [{, , "name":"edf"} ...

  7. 用MFC如何高效地绘图

    显示图形如何避免闪烁,如何提高显示效率是问得比较多的问题.而且多数人认为MFC的绘图函数效率很低,总是想寻求其它的解决方案.     MFC的绘图效率的确不高但也不差,而且它的绘图函数使用非常简单,只 ...

  8. 关于java reflect

    反射的基石 Class类 对比提问: Person类代表人,它的实例对象就是张三,李四这样一个个具体的人, Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class.对比 ...

  9. about oracle

    Oracle  劳伦斯.埃里森 Larry Ellison history: 人工管理阶段 文件管理阶段 数据库系统阶段 model:[模型是所研究的系统.过程.事物或概念的一种表达形式] 层次结构m ...

  10. Python Standard Library 学习(一) -- Built-in Functions 内建函数

    内建函数列表 Built-in Functions abs() divmod() input() open() staticmethod() all() enumerate() int() ord() ...