【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);
 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值