使用JavaScript闭包,以工厂模式实现定时器对象
原始对象写法
一般工作中写Javascript代码,主要写全局函数,并组织函数之间的调用,确实比较低级,
于是想利用面向对象的思想应用到JS编码中。
在火狐浏览器开发者网站上,看到一个实例利用对象技术实现的定时器例子,网址如下,
https://developer.mozilla.org/en-US/docs/Web/API/window.clearTimeout#Example
也贴出代码,如下,其实现是利用对象技术,在对象定义了三个函数(开始、取消 和 时间到的触发函数),
定时器句柄 timeoutID 作为定向的属性存储。
优点,自然比直接使用setTimeout函数更加集成,对功能做了封装,方便维护和管理。
缺点,只能支持一个计时器实例,且封装的过程,没有做到信息隐藏(对象中的 句柄 和 三个函数都可以被调用)。
<html>
<head>
<style> </style>
</head>
<body> <script type='text/javascript'>
var alarm = {
remind: function(aMessage) { alert(aMessage); delete this.timeoutID;
}, setup: function() {
this.cancel();
var self = this;
this.timeoutID = window.setTimeout(function(msg) {self.remind(msg);}, 1000, "Wake up!");
}, cancel: function() {
if(typeof this.timeoutID == "number") {
window.clearTimeout(this.timeoutID);
delete this.timeoutID;
}
}
};
window.onclick = function() { alarm.setup() };
</script>
</body>
</html>
闭包方法
下面应用工厂模式和闭包技术,解决上例中的缺点, 支持多实例计时器应用 并 明确开放定时器开放的接口。
如下代码中,
1、createTimer 为工厂函数 , 入参为计时结束触发的函数, 和options可选参数(目前只有timeout,默认为1秒);
2、每次调用createTimer生成一个定时器对象,即createTimer中 return出来的对象,
此对象定义的属性和函数,即为定时器开放接口 , 包括 start 和 stop。
3、定时器句柄 timerHandle 作为内部变量,不直接开放, 通过getTimerID函数访问,同时start赋值,被stop清空。
4、函数 fnAlarm ,也是作为内部变量,不开放,此为计时结束触发函数,createTimer第一参数(fnAlarmIn)的重载赋值,
此函数调用后会清空定时器句柄。
开放接口 start stop 和 getTimerID, 被return到createTimer之外,
同时它们对createTimer内部变量的timerHandle 和 fnAlarm的依赖,构成了闭包。
闭包我理解是构造出来的一个封闭的运行环境, 即被return出去的这些函数,依赖的运行环境仍然跟这些函数绑定,没有切换到全局环境中去,这些函数可以继续对被return之前的环境变量进行读写。
-- 概念中心是构造一个运行环境,此环境包括 全局变量 和 函数入参 和 函数局部变量。
闭包的定义: 参考 下面文档, 其中牵扯到定义环境和引用环境, 当不一样,才构成这个:
http://www.ibm.com/developerworks/cn/linux/l-cn-closure/index.html
显然闭包的技术天然含义满足设计模式中的工厂模式。
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
</head>
<body>
<label>timer msg :</label> </br>
<textarea id="timer_msg" style="width:500px; height:300px"></textarea>
<script>
$.fn.extend({printMsg : function(msg){
var oldmsg = $(this).val();
var newmsg = oldmsg + "\n" + msg;
$(this).val(newmsg);
}}); function createTimer(fnAlarmIn, options)
{
/* external settable */
var timeout = 1000;
var fnAlarm = function(){
$("#timer_msg").printMsg(timerHandle.toString()
+ "-please add custom fnAlarm")
}; /* inner maintain variable */
var timerHandle; /* set external variable */
if ( fnAlarmIn )
{
fnAlarm = fnAlarmIn;
} if ( options && options.timeout )
{
timeout = options.timeout;
} return {
start: function(){
timerHandle = setTimeout(function(){
fnAlarm();
delete this.timerHandle;
},timeout);
$("#timer_msg").printMsg(timerHandle.toString() + "-timer started");
},
stop: function(){
clearTimeout(timerHandle)
delete this.timerHandle;
$("#timer_msg").printMsg(timerHandle.toString() + "-timer stoped");
},
getTimerID: function(){
return timerHandle;
}
};
} var timer1 = createTimer();
timer1.start();
timer1.stop(); var timer2 = createTimer(function(){
$("#timer_msg").printMsg("timer custom alarm");
}, {timeout:2000});
timer2.start(); var timer3 = createTimer(function(){
$("#timer_msg").printMsg(timer3.getTimerID()+"-timer custom alarm");
}, {timeout:3000});
timer3.start();
</script>
</body>
</html>
改进
上例中,定时器内部的函数中的打印都带有定时器句柄前缀,目的是对于多实例情况下跟踪各个实例的运行状态;
定时器内部是这样,但是定时器触发函数中的打印是没有句柄的,如timer2中触发函数的打印;
如果需要句柄来区分,就像timer3中,利用getTimerID接口来获取,然后打印。
此获取句柄方法可行,但是每次都要使用 timer3.getTimerID() 来调用,显然不满足面向对象的思想,
因为此函数是在timer3创建之后的响应函数,理应使用 this.getTimerID() 来获取。
下面就来解决这个问题,首先参考获取网站上关于 setTimeout 计时结束触发函数中 this的解决办法,见下网址
https://developer.mozilla.org/zh-CN/docs/DOM/window.setTimeout
其基本思路为, 重载setTimeout,在新的函数中, 保存当前调用setTimeout的对象,
然后在真实的setTimeout的计时结束触发函数中,使用this来引用此对象,此时就构成了闭包。
引入setTimeout重载代码,并将setTimeout.call到定时器对象上, 并通过fnAlarm.call,将对象传递到用户自定义的函数中。
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
</head>
<body>
<label>timer msg :</label> </br>
<textarea id="timer_msg" style="width:500px; height:300px"></textarea>
<script>
$.fn.extend({printMsg : function(msg){
var oldmsg = $(this).val();
var newmsg = oldmsg + "\n" + msg;
$(this).val(newmsg);
}}); var __nativeST__ = window.setTimeout;
window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
var oThis = this, aArgs = Array.prototype.slice.call(arguments, 2);
return __nativeST__(vCallback instanceof Function ? function () {
vCallback.apply(oThis, aArgs);
} : vCallback, nDelay);
}; function createTimer(fnAlarmIn, options)
{
/* external settable */
var timeout = 1000;
var fnAlarm = function(){
$("#timer_msg").printMsg(timerHandle.toString()
+ "-please add custom fnAlarm")
}; /* inner maintain variable */
var timerHandle;
var timerObj; /* set external variable */
if ( fnAlarmIn )
{
fnAlarm = fnAlarmIn;
} if ( options && options.timeout )
{
timeout = options.timeout;
} return {
start: function(){
timerHandle = setTimeout.call(this, function(){
$("#timer_msg").printMsg(this.getTimerID() + "-->timer id");
fnAlarm.call(this);
delete this.timerHandle;
},timeout);
$("#timer_msg").printMsg(timerHandle.toString() + "-timer started");
},
stop: function(){
clearTimeout(timerHandle)
delete this.timerHandle;
$("#timer_msg").printMsg(timerHandle.toString() + "-timer stoped");
},
getTimerID: function(){
return timerHandle;
}
};
} var timer1 = createTimer();
timer1.start();
timer1.stop(); var timer2 = createTimer(function(){
$("#timer_msg").printMsg("timer custom alarm");
}, {timeout:2000});
timer2.start(); var timer3 = createTimer(function(){
$("#timer_msg").printMsg(timer3.getTimerID()+"-timer custom alarm");
}, {timeout:3000});
timer3.start(); var timer4 = createTimer(function(){
$("#timer_msg").printMsg(this.getTimerID()+"-timer custom alarm");
}, {timeout:4000});
timer4.start();
</script>
</body>
</html>
除了上面 的触发函数的对象绑定功能,
setTimeout执行函数带扩展参数,解决IE6、7不支持扩展参数的缺陷, 也是用闭包功能,见:
https://developer.mozilla.org/zh-CN/docs/DOM/window.setTimeout
使用JavaScript闭包,以工厂模式实现定时器对象的更多相关文章
- JavaScript面向对象OOM 2(JavaScript 创建对象的工厂模式和构造函数模式)
在创建对象的时候,使用对象字面量和 new Object() 构造函数的方式创建一个对象是最简单最方便的方式.但是凡是处于初级阶段的事物都会不可避免的存在一个问题,没有普适性,意思就是说我要为世界 ...
- [设计模式] javascript 之 抽象工厂模式
抽象工厂模式说明 1. 工厂方法模式的问题: 在工厂方法模式里,创建类都需要通过 工厂类,如果要扩展程序,就必须修改工厂类,这违背了闭包原则,对扩展开放,对修改关闭:对于设计有一定的问题. 2. 如何 ...
- JavaScript设计模式之工厂模式
一.工厂模式概念 工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类.该模式使一个类的实例化延迟到了子类.而子类可以重写接口方法以便创建的时候指定自己的对象类型(抽象工厂). 这个模 ...
- JavaScript设计模式--简单工厂模式
一,介绍 工厂模式创建对象(视为工厂里的产品)时无需指定创建对象的具体类. 工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类.该模式使一个类的实例化延迟到了子类.而子类可以重写接口 ...
- JavaScript设计模式(3)-工厂模式
工厂模式 1. 简单工厂 简单工厂:使用一个类或对象封装实例化操作 假如我们有个自行车商店类 BicycleShop,它提供了销售自行车的方法可以选择销售两类自行车 Speedster,Comfort ...
- JavaScript设计模式-10.工厂模式实例xhr
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- JavaScript 三种工厂模式
标签(空格分隔): JavaScript 简单工厂模式是工厂函数返回实例化对象或者对象,工厂函数作为一个方法. 工厂方法模式是工厂函数不作改变,将子类放在工厂原型中:工厂函数返回对应的实例化对象:re ...
- javascript设计模式-抽象工厂模式
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- javaScript设计模式之----工厂模式
什么是工厂模式?我们通过一个例子了解一下: 比如我们想要弹出几个字符串 function funA(){ alert('a'); } function funB(){ alert('b'); } fu ...
随机推荐
- 那么都数据库表,那么多不同记录。是怎样都存储在一个key-value数据库的?
那么都数据库表,那么多不同记录.是怎样都存储在一个key-value数据库的? :设置不同的键值而已!不同的表,选出统一的key规范 jedis.sadd("tom:friend:list& ...
- uitextfield输入字符限制
-(UITextField*)createField:(NSString*)placeholder andTag:(int)tag andFont:(double)font{ UITextField ...
- Socket简介
本文全部摘抄http://c.biancheng.net/cpp/html/3029.html 大多数项目是在Linux下开发服务器端,而在Windows下开发客户端,需要经常在两大平台之间进行切换, ...
- filesort
- 【php学习】array_map,array_walk,array_filter的区别
array_map(function($v){return $v+1;}, $array); array_walk($array, function($v, $k){...}); array_filt ...
- 原生js实现跑马灯抽奖效果
目前好多的微信活动都有一些抽奖活动,其中就有跑马灯. <!DOCTYPE html> <html> <head> <title>跑马灯效果</ti ...
- 【转】Unity3D中脚本的执行顺序和编译顺序(vs工程引用关系)
http://www.cnblogs.com/champ/p/execorder.html 在Unity中可以同时创建很多脚本,并且可以分别绑定到不同的游戏对象上,它们各自都在自己的生命周期中运行.与 ...
- 【转】NGUI版虚拟摇杆
http://blog.csdn.net/anyuanlzh/article/details/40107577 下面是我用nui实现的一个虚拟摇杆. 1,示图 2.代码如下,都有比较详细的注释,就不说 ...
- [daily][network] NAT原理(转)
写在转发之前: 一直以来,我一直有一个疑惑,SNAT的时候,如果两个内网主机恰巧使用了相同的源端口号该怎么办呢? 我自己猜测的方法是改掉一个端口号,把端口一起映射(当然还有另一个设想,就是把包同时广播 ...
- HIVE中的HQL操作
1.字段查询 select empno,ename from emp; 2.过滤where,limit,distinct select * from emp where sal >2500; s ...