精华帖 (7) :: 良好帖 (5) :: 新手帖 (3) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-08-15
最后修改:2009-08-21
前几天帮erricwang写一个js计时器,发现可以用有很多种方式实现相同的功能。现将它写下来,和大家一起探讨分析比较。
实现一:
且看第一种实现,显然这里有一个很不好的地方,计时完成之后,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);
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间: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次也是非常不爽的. |
|
返回顶楼 | |
发表时间:2009-08-15
最后修改:2009-08-16
ls说的很有道理,一针见血,好像确实每次实例化都会被构造一次,带来比较差的性能,欢迎继续拍砖。
|
|
返回顶楼 | |
发表时间:2009-08-16
最后修改: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--; } } |
|
返回顶楼 | |
发表时间: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 |
|
返回顶楼 | |
发表时间:2009-08-16
最后修改: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,而不是每次通过返回一个新的函数来获取变量(实例对象) |
|
返回顶楼 | |
发表时间:2009-08-16
最后修改: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--; } } } } |
|
返回顶楼 | |
发表时间:2009-08-16
zbm2001 写道 谁告诉你的?当然你也可以清空一下定时器
this.timer = null; 设计模式的问题,二楼已给出提示,你也注意到了,不再赘述。 我给的一些修改,只是顺带提醒一下javascript编程中的需要注意的地方: 1.节约使用DOM获取元素,尽可能的避免重复获取; 2.巧用javascript表达式/运算符和内置的类型转换,为程序提速,同时也使得代码内敛; 3.合理利用闭包。 代码很到位,欢迎继续拍砖!javaeye果然人才出没,欢迎继续提供更优雅的实现方式! |
|
返回顶楼 | |
发表时间:2009-08-16
ls说的也很中肯。在此引入闭包,只是引入js的一种常用实现方式而已,如果用在本例确实牵强。闭包更多的是实现真正的私有的作用,而闭包可能带来的内存泄露等问题确实值得注意。
|
|
返回顶楼 | |
发表时间:2009-08-16
楼上2为对js真是研究透彻啊~!
|
|
返回顶楼 | |