前几天帮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------------------------------------------------------------
继续收集评论中的实现方式,优劣请读者自评
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);
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);