`
jianguang_qq
  • 浏览: 91817 次
  • 性别: Icon_minigender_1
  • 来自: 深圳南山
社区版块
存档分类
最新评论

【JS优化系列】从一个计时器的写法探讨js多种实现方式的优劣

阅读更多

      前几天帮erricwang写一个js计时器,发现可以用有很多种方式实现相同的功能。现将它写下来,和大家一起探讨分析比较。

 

实现一:

<body>
<div id="time1">time1</div>
<div id="time2">time2</div>
</body>
<script>
function Timer(id){
	this.id = id;
	this.begin = function(count){
		this.show(this.id, count)();
		setInterval(this.show(this.id, count-1),1000);
	}
	this.show = function(id, count){
		return function(){
			document.getElementById(id).innerHTML = count<0 ? "over" :count;
			count--;
		}
	}
} 

t1 = new Timer("time1");
t1.begin(50);

t2 = new Timer("time2");
t2.begin(10);
</script>

 

   且看第一种实现,显然这里有一个很不好的地方,计时完成之后,setInterval没有被clear,严重影响了性能。

 

实现二:

function Timer(id){
	this.id = id;
	this.timer = null;
	this.count = 0;
	this.begin = function(count){
		this.count = count;
		this.show(this)();
		this.timer = setInterval(this.show(this),1000);
	}
	this.show = function(obj){	
		return function(){
			if(obj.count < 0){
				document.getElementById(obj.id).innerHTML = "over";
				clearInterval(obj.timer);
				return ;
			}
			document.getElementById(obj.id).innerHTML = obj.count;
			obj.count--;
		}
	}
} 

       实现二解决了实现一的setInterval未被clear的问题,看上去也很“完美”,且看下面:

alert(t1.show == t2.show) //结果false
alert(t1.begin == t2.begin) //结果false

      很明显这里Timer每个实例都复制了一份show和begin,二不是共享同一份的,所以t1.showt和2.show所指向的并非同一份实现,这样对性能多少会有些影响。

 

实现三:

function Timer(id){
	this.id = id;
	this.timer = null;
	this.count = 0;
	this.begin = function(count){
		this.count = count;
		Timer.show(this)();
		this.timer = setInterval(Timer.show(this),1000);
	}
	Timer.show = function(obj){	
		return function(){
			if(obj.count < 0){
				document.getElementById(obj.id).innerHTML = "over";
				clearInterval(obj.timer);
				return ;
			}
			document.getElementById(obj.id).innerHTML = obj.count;
			obj.count--;
		}
	}
} 

      这里实现了让所有实例共享一份show函数:

alert(t1.begin == t2.begin) //结果true 

 

实现四:

 

function Timer(id){
	this.id = id;
	this.timer = null;
	this.count = 0;
	this.begin = function(count){
		this.count = count;
		this.show(this)();//注意和实现三的区别:这里不是Timer.show(this)();
		this.timer = setInterval(this.show(this),1000);//注意和实现三的区别:这里不是Timer.show(this)();
	}
} 
Timer.prototype.show = function(obj){	
	return function(){
		if(obj.count < 0){
			document.getElementById(obj.id).innerHTML = "over";
			clearInterval(obj.timer);
			return ;
		}
		document.getElementById(obj.id).innerHTML = obj.count;
		obj.count--;
	}
}

      实现三和实现四很有意思:二者都实现了让所有实例共享一份show方法。

      区别是:实现三show作为Timer对象的一个属性,这里有点类似与java的静态方法,show方法是属于Timer对象本身的,而不是其实例(t1、t2...)的,所以需要用Timer.show(..)来调用;而实现四采用了“原型”prototype方式来实现,这里show方法不是Timer对象本身,而是其实例(t1、t2...)的,所以使用this.show(..)或者t1.show(...)来调用,这里很有意思“原型”创建的属性是属于其实例的,而且所有实例共享同一份实现(不知道我这里的理解是否正确,欢迎大家拍砖)。这多少有些让学java等面向对象语言的人难以理解,而这也正式js有意思的地方。

      那么这两种实现方式那种更优呢?欢迎各位讨论。

 

实现五:

 

function Timer(id){
	this.id = id;
	this.timer = null;
	this.count = 0;

}
Timer.prototype.begin = function(count){
	this.count = count;
	this.show(this)();//注意这里不是Timer.show(this)();
	this.timer = setInterval(this.show(this),1000);//注意这里不是Timer.show(this)();
}
Timer.prototype.show = function(obj){	
	return function(){
		if(obj.count < 0){
			document.getElementById(obj.id).innerHTML = "over";
			clearInterval(obj.timer);
			return ;
		}
		document.getElementById(obj.id).innerHTML = obj.count;
		obj.count--;
	}
} 

      这里将begin也使用原型实现,可能这种写法让你看起来不太舒服,那我们换一种。

 

实现六:

 

function Timer(id){
	this.id = id;
	this.timer = null;
	this.count = 0;
}
Timer.prototype = {
	begin : function(count){
		this.count = count;
		this.show(this)();//注意这里不是Timer.show(this)();
		this.timer = setInterval(this.show(this),1000);//注意这里不是Timer.show(this)();
	},
	show : function(obj){	
		return function(){
			if(obj.count < 0){
				document.getElementById(obj.id).innerHTML = "over";
				clearInterval(obj.timer);
				return ;
			}
			document.getElementById(obj.id).innerHTML = obj.count;
			obj.count--;
		}
	} 
}

      或者,再换一种。

 

实现七:

 

function Timer(id){
	this.id = id;
	this.timer = null;
	this.count = 0;
	Timer.prototype.begin = function(count){
		this.count = count;
		this.show(this)();//主要这里不是Timer.show(this)();
		this.timer = setInterval(this.show(this),1000);//主要这里不是Timer.show(this)();
	}
	Timer.prototype.show = function(obj){	
		return function(){
			if(obj.count < 0){
				document.getElementById(obj.id).innerHTML = "over";
				clearInterval(obj.timer);
				return ;
			}
			document.getElementById(obj.id).innerHTML = obj.count;
			obj.count--;
		}
	} 
}

       这方式,看起来是不是更优雅一些呢。以上都采用面向对象的方式来实现的。那我们是否还可以采用其他方式实现呢? 

 

实现八:

var Timer = {
	begin : function(id,count){
		var obj = {};
		obj["id"] = id;
		obj["count"] = count;
		Timer.show(obj)();
		obj["timer"] = setInterval(Timer.show(obj),1000);//注意这里不是Timer.show(this)();
	},
	show : function(obj){	
		return function(){
			if(obj["count"] < 0){
				document.getElementById(obj["id"]).innerHTML = "over";
				clearInterval(obj["timer"]);
				return ;
			}
			document.getElementById(obj["id"]).innerHTML = obj["count"] ;
			obj["count"]--;
		}
	}
}

Timer.begin("time1", 30);
Timer.begin("time2", 60);

       这里采用了对象字面量的方式来实现的。对象字面量其实是一种单例模式,用在这里其实很变扭,本文这里引入只是引入一种js实现方式而已,仅供大家探讨。下一例也一样。

 

实现九:

 

var Timer = (function(){
	var items = {};
	function begin(id,count){
		var obj = {};
		obj["id"] = id;
		obj["count"] = count;
		Timer.show(obj)();
		obj["timer"] = setInterval(Timer.show(obj),1000);//注意这里不是Timer.show(this)();
		Timer.items[id] = obj;
	};
	function show(obj){	
		return function(){
			if(obj["count"] < 0){
				document.getElementById(obj["id"]).innerHTML = "over";
				clearInterval(obj["timer"]);
				return ;
			}
			document.getElementById(obj["id"]).innerHTML = obj["count"] ;
			obj["count"]--;
		}
	}
	return {
		items : items,
		begin : begin,
		show : show
	}
})()

Timer.begin("time1", 30);
Timer.items["time1"]["count"] = 80;//重新从80开始计时
Timer.begin("time2", 60);

       这里其实也是采用的对象字面量的方式来实现的,只是采用了闭包而已,应用闭包可以真正的实现属性私有化(不过这里没有体现出来)。这里还加了items属性,让其所有实例保存起来,以便后面还可以调用。

 

      平常用的比较多的还有最后一种闭包结合对象字面量的方式(不过在这个场景个人觉得用面向对象方式来实现更好一些)。对象字面量的方式其实也是js的单例模式,在js里应用很广泛,不过在这里应用好像不太合适,看这里的最后两个实现都觉得很牵强。

      从一个小小的计时器,可以衍生出如此多的方式来实现,真让人惊叹于js表现形式之丰富。如此众多实现方式,都有其优点和缺点,在不同的场合也有不同的应用。这里只是表达了一下个人的理解和观点,欢迎大家一起讨论js各种实现方式的优缺点及其适用场合。也欢迎拍砖,或者提供更好的实现方法。

 

 

【本人发帖抛砖引玉,希望能够引出更多的“玉”来,希望所写的每一段代码都能够得到一种最“优雅”的实现方式。以后本人会抛出更多的“砖”,希望能引来更多的“玉”,以供大家一起学习进步】

 

-------------------------------------------------------2009.08.17------------------------------------------------------------

 

到目前为止,个人觉得3楼zbm2001 的实现最优,让我学到了不少,希望能有更多的牛人来提供更优的实现。

function Timer(id){
	this.element = document.getElementById(id);
	this.timer = null;
	this.count = 0;
}
Timer.prototype = {
	begin : function(count){
		this.count = count;
		this.show();
		var _this = this;
		this.timer = setInterval(function(){_this.show();}, 1000);
	}
	,
	show : function(){
		this.element.innerHTML = this.count < 0 ? clearInterval(this.timer) || "over" : this.count--;
	} 
}

 

 

-------------------------------------------------------2009.08.21------------------------------------------------------------

继续收集评论中的实现方式,优劣请读者自评

janpoem

var Timer = function(id) {
	
	var _step = 500, _count = 0, _ticktark = 0, _element = document.getElementById(id);
	
	function __clear() {
		if (_ticktark != null) clearInterval(_ticktark);
	}
	
	return {
		
		begin: function(count, step) {
			if (_element && count > 0) {
				// 看看起始值多少,主要看看有没有被污染
				console.log('on start:', 'count:', _count, 'step:', _step);
				__clear();
				_step = step;
				_count = count;
				// 再看看
				console.log('on set:', 'count:', _count, 'step:', _step);
				_ticktark = setInterval(this.show, _step)
			}
			return this;
		},
		
		show: function() {
			_element && (_element.innerHTML = _count > 0 ? _count-- : 'over');
			if (_count <= 0) {
				console.log(_count);
				__clear();
			}
			return this;
		}
	}
}	

Timer('time1').begin(20, 100);
Timer('time2').begin(30, 200);

 

lifesinger

function Timer(id) {
    this.container = document.getElementById(id);
}

Timer.prototype = {

    constructor: Timer,

    begin: function(count) {
        var container = this.container;
        setTimeout(function() {
            container.innerHTML = count > 0 ? count-- : "over";
            if(count + 1) {
                setTimeout(arguments.callee, 1000);
            }
        }, 1000);
    }
    
};

new Timer("time1").begin(10);
 

 

 

分享到:
评论
12 楼 sw1982 2009-08-16  
呵呵。剑光也搬到javaeye了?
11 楼 bigcoon 2009-08-16  
其实我觉得没意思。。。
10 楼 Bernard 2009-08-16  
http://bbs.51js.com/viewthread.php?tid=78751&page=1&fromuid=87718#pid556989

以前写的……现在看起来好丑-_-#
9 楼 whaosoft 2009-08-16  
楼上2为对js真是研究透彻啊~!
8 楼 jianguang_qq 2009-08-16  
ls说的也很中肯。在此引入闭包,只是引入js的一种常用实现方式而已,如果用在本例确实牵强。闭包更多的是实现真正的私有的作用,而闭包可能带来的内存泄露等问题确实值得注意。
7 楼 jianguang_qq 2009-08-16  
zbm2001 写道
谁告诉你的?当然你也可以清空一下定时器
this.timer = null;

设计模式的问题,二楼已给出提示,你也注意到了,不再赘述。

我给的一些修改,只是顺带提醒一下javascript编程中的需要注意的地方:

1.节约使用DOM获取元素,尽可能的避免重复获取;
2.巧用javascript表达式/运算符和内置的类型转换,为程序提速,同时也使得代码内敛;
3.合理利用闭包。


代码很到位,欢迎继续拍砖!javaeye果然人才出没,欢迎继续提供更优雅的实现方式!
6 楼 zbm2001 2009-08-16  
另外,如果你偏爱传统的面向对象的设计模式,习惯将方法打包在类中,可以初始化定义一下:

function Timer(id){ 
    this.id = id; 
    this.timer = null; 
    this.count = 0; 
if(Timer.prototype.initialization !== true){
Timer.prototype.initialization = true;
    Timer.prototype.begin = function(count){ 
        this.count = count; 
        this.show(this)();//主要这里不是Timer.show(this)(); 
        this.timer = setInterval(this.show(this),1000);//主要这里不是Timer.show(this)(); 
    } 
    Timer.prototype.show = function(obj){    
        return function(){ 
            if(obj.count < 0){ 
                document.getElementById(obj.id).innerHTML = "over"; 
                clearInterval(obj.timer); 
                return ; 
            } 
            document.getElementById(obj.id).innerHTML = obj.count; 
            obj.count--; 
        } 
    }
}  

5 楼 zbm2001 2009-08-16  
谁告诉你的?当然你也可以清空一下定时器
this.timer = null;

设计模式的问题,二楼已给出提示,你也注意到了,不再赘述。

我给的一些修改,只是顺带提醒一下javascript编程中的需要注意的地方:

1.节约使用DOM获取元素,尽可能的避免重复获取;
将DOM元素保存在实例对象的hash中。

2.巧用javascript表达式/运算符和内置的类型转换,为程序提速,同时也使得代码内敛;
this.element.innerHTML = this.count < 0 ? clearInterval(this.timer) || "over" : this.count--;
三元表达式 + 短路运算

3.合理利用闭包。
var _this = this; 
        this.timer = setInterval(function(){_this.show();}, 1000);
每次执行只需获取一个实例的环境变量this,而不是每次通过返回一个新的函数来获取变量(实例对象)
4 楼 jianguang_qq 2009-08-16  
zbm2001 写道
function Timer(id){
	this.element = document.getElementById(id);
	this.timer = null;
	this.count = 0;
}
Timer.prototype = {
	begin : function(count){
		this.count = count;
		this.show();
		var _this = this;
		this.timer = setInterval(function(){_this.show();}, 1000);
	}
	,
	show : function(){
		this.element.innerHTML = this.count < 0 ? clearInterval(this.timer) || "over" : this.count--;
	} 
}

这里setInterval没有被clear
3 楼 zbm2001 2009-08-16  
function Timer(id){
	this.element = document.getElementById(id);
	this.timer = null;
	this.count = 0;
}
Timer.prototype = {
	begin : function(count){
		this.count = count;
		this.show();
		var _this = this;
		this.timer = setInterval(function(){_this.show();}, 1000);
	}
	,
	show : function(){
		this.element.innerHTML = this.count < 0 ? clearInterval(this.timer) || "over" : this.count--;
	} 
}
2 楼 jianguang_qq 2009-08-15  
ls说的很有道理,一针见血,好像确实每次实例化都会被构造一次,带来比较差的性能,欢迎继续拍砖。
1 楼 02221021 2009-08-15  
第七种方法

Timer.prototype = {  
6.        begin : function(count){  
7.            this.count = count;  
8.            this.show(this)();//注意这里不是Timer.show(this)();  
9.            this.timer = setInterval(this.show(this),1000);//注意这里不是Timer.show(this)();  
10.        }  
11.        show : function(obj){     
12.            return function(){  
13.                if(obj.count < 0){  
14.                    document.getElementById(obj.id).innerHTML = "over";  
15.                    clearInterval(obj.timer);  
16.                    return ;  
17.                }  
18.                document.getElementById(obj.id).innerHTML = obj.count;  
19.                obj.count--;  
20.            }  
21.        }  
22.    }  


这个超大的对象怎么能写到构造函数里啊,虽然没产生多余的副本,但是被构造2次也是非常不爽的.

相关推荐

    javascript实现计时器

    "JavaScript实现计时器"这个主题涉及到JavaScript编程中的一个常见任务:创建一个可以随着时间流逝更新的计时器。计时器在各种网页应用中都有广泛的应用,如倒计时、实时显示时间等。下面我们将深入探讨如何使用...

    js计时器数字卡片翻转秒表计时器网页特效

    "js计时器数字卡片翻转秒表计时器网页特效"就是一个利用JavaScript实现的动态计时功能,它结合了数字卡片翻转的效果,使得秒表计时更为直观且具有视觉吸引力。这种效果常见于体育竞赛、健身应用或者任何需要实时计时...

    css3圆形进度条时分秒计时器js特效

    总结,这款“css3圆形进度条时分秒计时器js特效”充分利用了CSS3的高级特性,结合JavaScript的时间管理和DOM操作,实现了动态的、可视化的计时功能。这样的特效不仅可以提升用户体验,也可以在各种应用场景中增加...

    【JavaScript源代码】JS实现一个秒表计时器.docx

    在JavaScript中,实现一个秒表计时器涉及到定时器(setTimeout和setInterval)以及DOM操作。下面我们将详细探讨这些知识点: 1. **定时器:** - `setInterval` 函数用于每隔一定时间执行一次指定的函数。在这个...

    JS CSS3实现苹果iwatch计时器.zip

    在本项目"JS CSS3实现苹果iwatch计时器.zip"中,我们主要探讨的是如何使用JavaScript(JS)和CSS3技术来模拟苹果iWatch的计时器功能,包括开始、暂停以及重置操作,并能记录计时次数。这个项目对于前端开发者来说,...

    js css3圆形的时分秒计时器动画特效

    本文将详细探讨如何利用这两者来实现一个圆形的时分秒计时器动画特效。 首先,我们需要了解JavaScript(JS)的基础。JavaScript是一种解释型的、面向对象的、弱类型的脚本语言,常用于网页和网络应用开发。它能够...

    js计时器 (脚本语言)

    JavaScript(简称JS)是网页开发中的重要脚本语言,它提供了丰富的功能,其中之一就是计时器功能。在JavaScript中,计时器常用于实现动画效果、执行定时任务、或者跟踪时间流逝等。本文将深入探讨JavaScript计时器的...

    js实现简易计时器

    自己实现的简单的倒计时器,发现网上关于这方面的代码有...我自己用js手写了一个分享给大家,界面比较简洁,后面会再写一个比较酷炫的。如果没有积分还想下载的小伙伴可以加qq邮箱联系我,我发给你:1343121616@qq.com

    计时器实现

    总结来说,计时器在各种编程语言和应用领域都有着广泛的应用,其实现方式多种多样,从简单的循环检查到复杂的事件驱动,都需要根据具体需求和环境来选择合适的方法。理解并掌握计时器的原理和实现技巧,对于提升软件...

    javascript 秒表计时器实现代码

    接下来,我们将深入探讨如何使用JavaScript来实现一个基本的秒表计时器功能。在上述提供的代码示例中,我们能够看到创建一个秒表计时器所依赖的关键元素和方法。 首先是HTML部分,这是用户界面的基础。在`&lt;body&gt;`...

    javascript秒表计时器

    以下是一个简单的JavaScript秒表计时器的实现: ```javascript let startTime = null; let elapsedTime = 0; let timerInterval = null; function startTimer() { startTime = new Date(); timerInterval = ...

    简单清新的纯JS实现的计时特效电子计时效果源码下载

    总的来说,这个"简单清新的纯JS实现的计时特效电子计时效果"源码提供了一个学习和实践JavaScript计时器功能的平台。通过理解和研究源码,开发者不仅可以掌握基本的计时器实现,还能进一步了解JavaScript与DOM的交互...

    javascript 实现网页 倒计时 代码

    本篇文章将详细介绍如何利用JavaScript编写一个网页倒计时功能,并结合提供的`index.html`和`js`文件进行解析。 首先,我们需要理解JavaScript中的时间处理。JavaScript使用`Date`对象来表示日期和时间,它提供了...

    js实现倒计时

    javascript实现倒计时

    多功能计时器-倒计时器

    "多功能计时器-倒计时器"这款应用程序,正如其标题所示,旨在提供一种高效且灵活的计时解决方案,适应多种场景的需求。 首先,我们来详细探讨倒计时器的核心功能。倒计时器是一种能够从预设时间开始向零倒数的计时...

    JS 系列计时器资料.rar

    "JS系列计时器资料.rar"这个压缩包包含了关于JavaScript计时器的详细文档,名为"5.JS计时器.doc"。以下将深入探讨JavaScript中的计时器机制及其应用。 1. **setTimeout()和setInterval()** - `setTimeout()`: 这个...

    理想Web倒计时器的设计与实现

    通过上述代码实现,我们能够构建一个既能够在不同客户端间保持同步,又具备防刷新、防关闭和自校正功能的理想Web倒计时器。这种方式不仅提高了用户体验,也为Web开发者提供了更为灵活可靠的解决方案。

    jQuery实现的倒计时器

    在这个项目中,我们将深入探讨如何使用jQuery实现一个倒计时器。 首先,我们需要理解jQuery的基本使用。jQuery通过简洁的语法来处理DOM操作、事件处理和动画效果。例如,我们可以使用`$`符号来选择元素,然后对这些...

    Android计时器实现

    本文将深入探讨如何在Android中实现计时器功能,主要以`HLQCountDownTimer`为例,这是一个自定义的倒计时计时器类。 ### 一、Android计时器基础 在Android中,有多种计时器实现方式,包括`java.util.Timer`、`...

    djs.rar_DJS哪里的时间_djs_倒计时_倒计时器_倒计时计时器

    本文将详细探讨“DJS倒计时器”,一种小巧而实用的计时工具,以及它的功能、实现原理和使用方法。 首先,DJS倒计时器的核心功能在于设定并显示一个特定时间点前的剩余时间。用户可以根据自己的需求,设定一个未来的...

Global site tag (gtag.js) - Google Analytics