原始对象写法

  一般工作中写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闭包,以工厂模式实现定时器对象的更多相关文章

  1. JavaScript面向对象OOM 2(JavaScript 创建对象的工厂模式和构造函数模式)

      在创建对象的时候,使用对象字面量和 new Object() 构造函数的方式创建一个对象是最简单最方便的方式.但是凡是处于初级阶段的事物都会不可避免的存在一个问题,没有普适性,意思就是说我要为世界 ...

  2. [设计模式] javascript 之 抽象工厂模式

    抽象工厂模式说明 1. 工厂方法模式的问题: 在工厂方法模式里,创建类都需要通过 工厂类,如果要扩展程序,就必须修改工厂类,这违背了闭包原则,对扩展开放,对修改关闭:对于设计有一定的问题. 2. 如何 ...

  3. JavaScript设计模式之工厂模式

    一.工厂模式概念 工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类.该模式使一个类的实例化延迟到了子类.而子类可以重写接口方法以便创建的时候指定自己的对象类型(抽象工厂). 这个模 ...

  4. JavaScript设计模式--简单工厂模式

    一,介绍 工厂模式创建对象(视为工厂里的产品)时无需指定创建对象的具体类. 工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类.该模式使一个类的实例化延迟到了子类.而子类可以重写接口 ...

  5. JavaScript设计模式(3)-工厂模式

    工厂模式 1. 简单工厂 简单工厂:使用一个类或对象封装实例化操作 假如我们有个自行车商店类 BicycleShop,它提供了销售自行车的方法可以选择销售两类自行车 Speedster,Comfort ...

  6. JavaScript设计模式-10.工厂模式实例xhr

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  7. JavaScript 三种工厂模式

    标签(空格分隔): JavaScript 简单工厂模式是工厂函数返回实例化对象或者对象,工厂函数作为一个方法. 工厂方法模式是工厂函数不作改变,将子类放在工厂原型中:工厂函数返回对应的实例化对象:re ...

  8. javascript设计模式-抽象工厂模式

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. javaScript设计模式之----工厂模式

    什么是工厂模式?我们通过一个例子了解一下: 比如我们想要弹出几个字符串 function funA(){ alert('a'); } function funB(){ alert('b'); } fu ...

随机推荐

  1. lua5.2版本在VS2010下的环境搭建

    第一次使用脚本语言,第一次使用解释性语言 公司的现在维护的游戏开发的比较早,采用的lua还是比较早的版本,像NPC脚本系统就是使用lua+luabind结合的module模式,服务器端没涉及到,所以不 ...

  2. font awesome

    http://stackoverflow.com/questions/21406538/how-to-use-font-awesome-icons-from-node-modules

  3. 不遗留问题-menu数据拼装-2

    $res = array(); foreach($idlist_1 as $id1) { $tmp = array(); $tmp1 = array(); $tmp1[] = $id1; foreac ...

  4. Liunx 下使用cmake

    参考 http://blog.chinaunix.net/uid-28458801-id-3501768.html http://www.ibm.com/developerworks/cn/linux ...

  5. Qt 之 QQ系统表情(五)

    http://blog.csdn.net/goforwardtostep/article/details/52456276

  6. .NET 可空值类型

    Microsoft在CLR中引入了可空值类型(nullable value type)的概念. FCL中定义System.Nullable<T>类如下: [Serializable,Str ...

  7. 【VC6】【集成工具】将输入信息集成到VC工具中

    1.首先写一个工具,可以接受外部参数, 并且输入格式必须是固定的“"%s(%d):\n", __FILE__, __LINE__”形式. 2.编译生成EXE准备进行使用: 3.在V ...

  8. C++ 自动指针 共享指针

    #include <iostream> #include <string> #include <memory> class Item { public: Item( ...

  9. Github简明教程(转)

    原文地址 : http://wuyuans.com/2012/05/github-simple-tutorial/ github是一个基于git的代码托管平台,付费用户可以建私人仓库,我们一般的免费用 ...

  10. 通过自定义属性存储数据实现输入框获得焦点与失去焦点改变value值

    http://gopro.ee.cagoe.com/index.html     html: <div class="name"><input value=&qu ...