js中singleton模式解析及运用

news/2024/7/20 13:44:54 标签: javascript, 内存管理

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

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

                                                                   

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

                                      

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

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

    

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; }

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

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

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

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

 

javascript">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即可。

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

  

javascript">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()一样,保存在内存中。于是我们可以这么做:

javascript">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):

 

javascript"><!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>

 

  

 

转载于:https://www.cnblogs.com/yxy99/p/4852532.html


http://www.niftyadmin.cn/n/1510020.html

相关文章

盐城市计算机二级,江苏省盐城市2020上半年计算机二级报名时间|网上报名入口【12月17日12时开通】...

&nbsp&nbsp[导读]:江苏省盐城市2020上半年计算机二级报名时间|网上报名入口【12月17日12时开通】&#xff0c;更多江苏等级考试报名时间、考试时间以及模拟试题&#xff0c;请访问易考吧江苏等级考试栏目江苏省盐城市2020上半年计算机二级报名时间|网上报名入口【12月1…

ubuntu wireshark找不到网卡及开启IP转发

wireshark找不到网卡问题&#xff1f; 解决办法1&#xff1a;sudo wireshark 解决办法2&#xff1a; 1&#xff09;添加用户组&#xff0c;我以wireshark为例 sudo groupadd wireshark 2&#xff09;将dumpcap更改为wireshark用户组 sudo chgrp wireshark /usr/bin/dumpcap 3&a…

高考科类中的计算机类,@所有高中生!新高考怎么选科?计算机类专业该怎么选科?...

计算机类专业不仅是各大高校的热门专业&#xff0c;在今年的求职行业榜单中也位列第一&#xff0c;同时是各大企业竞相需要的专业人才。因此&#xff0c;对于从2018年启动新高考的广东省来说&#xff0c;学计算机类或相关专业&#xff0c;如何选科的问题就成为关键的一环。主要…

计算机设计辅助 CAD 试题汇编,计算机辅助设计试题汇编-第四单元.doc

计算机辅助设计试题汇编-第四单元第四单 元图形编辑4.1第一题打开X:\CADTK中的图形文件Scad4-1.dwg(如左图)&#xff0c;并将其编辑成右图。将完成的图形以Tcad4-1.dwg为文件名存入考生自己的文件夹。4.2第二题打开X:\CADTK中的图形文件Scad4-2.dwg(如上图&#xff0c;图中所有…

PLSQL计算质数

看到有人实现了一个计算质数的函数&#xff0c;就是效率有点差&#xff0c;贴一个以前写的计算质数的算法。目标很简单&#xff0c;列出100以内的质数。其实算法很简单&#xff0c;两个循环就搞定了。但是发现使用不同的算法&#xff0c;执行效率差别之大相当惊人&#xff0c;特…

计算机专业高考450分能考郑州吗,2021年河南高考文科450分能上什么大学 成绩450分能上的学校有哪些...

高考结束就是最大的事情就是填报志愿了&#xff0c;文科理科不同分数选择也大不相同。2021年河南高考文科450分能上什么大学 成绩450分能上的学校有哪些。其实高考450多分可以选择的学校有很多。下面就来为大家分享高考450多分怎么办!!供大家参考!!河南高考文科450分能上什么大…

没有要使用计算机,计算机上没有插入键,要替换什么?

我的计算机上没有插入按钮&#xff0c;主页按钮&#xff0c;结束按钮您应该使用笔记本电脑&#xff0c;对吧&#xff1f;您的目的应该是能够使用数字键盘.1. 普通键盘将具有: Numlock按钮&#xff0c;单击以切换数字键盘的功能.2. 如果是&#xff0c;请使用小型数字键盘的数字功…

偏执却管用的10条Java编程技巧

本文由 ImportNew - LynnShaw 翻译自 javacodegeeks。欢迎加入翻译小组。转载请见文末要求。 经过一段时间的编码&#xff08;咦&#xff0c;我已经经历了将近20年的编程生涯&#xff0c;快乐的日子总是过得很快&#xff09;&#xff0c;我们开始感谢那些好习惯。因为&#xff…