setTimeout和setInterval的区别以及如何写出效率高的倒计时
1.setTimeout和setInterval都属于js中的定时器,可以规定延迟时间再执行某个操作,不同的是setTimeout在规定时间后执行完某个操作就停止了,而setInterval则可以一直循环执行下去。
下面介绍一下两种定时器的语法:
setTimeout(expression,milliseconds) 以及 setInterval(expression,milliseconds)
其中 expression是将要执行的某一项操作,而milliseconds则是延迟时间,expression可以是一个将要执行的函数名,也可以是一个字符串
比如:
1 function fun(){
2 alert('hello');
3 }
4 setTimeout(fun,1000); //参数是函数名
5 setTimeout('fun()',1000); //参数是字符串
6 setInterval(fun,1000);
7 setInterval('fun()',1000);
在上面代码中,无论是setTimeout还是setInterval,在使用函数名作为调用句柄时不能带参数,使用字符串调用时可以带参数,如:setTimeout('fun(name)',1000);
但是有些场合会要求必须使用函数名作为调用句柄,此时可以用另一种方法带参数:
方法一:
function fun(name){ alert('hello'+' ' +name); } function _fun(name){ return function(){ fun(name); } } setTimeout(_fun('Tom'),1000); //参数是函数名
方法二:
不用单独再定义一个函数,直接将函数调用放在一个函数里面,可以使用函数名作为调用句柄:
function fun(name){ alert('hello'+' ' +name); } setTimeout(function(){ fun('Tom'); },1000); //参数是函数名
setInterval使用方式一样,就不再多写了。
在以上代码中,setTimeout和setInterval的区别就是 setTimeout延迟一秒弹出'hello',之后便不再运行;而setInterval则会每隔一秒钟弹出'hello',直至用clear来清除
清除定时器的语法:
定时器会返回一个id,只要对这个id进行清除的操作即可:
1 function fun(){
2 alert('hello');
3 }
4 var t1 = setTimeout(fun,1000);
5 var t2 = setInterval(fun,1000);
6 clearTimeout(t1);
7 clearInterval(t2);
2. 用setTimeout实现setInterval的功能
虽然setTimeout只在延时时间后执行一次,但是我们可以使用递归调用的方法实现循环调用,实现类似setInterval的功能,例:
1 function fun(){
2 alert('hello');
3 setTimeout(fun,1000);
4 }
5 fun();
以上代码的功能是每隔一秒钟页面便弹出“hello”,相当于setInterval的循环间隔调用。
但是即使可以实现同样的功能,两种定时器也还是有运行方面的差别:
比如延时1s执行函数fun(),但是函数内部的操作执行需要的时间是2S,由于JavaScript是单线程的,那在这次执行完之前,下一次的循环便被阻塞了,处在排队的状态,当此次操作执行完以后才会执行处在排队状态的操作,
但是排队的序列太多,阻塞结束以后只能执行一个,这样会造成性能的浪费,
所以在这样的情况下,使用setTimeout递归调用实现循环的方法便显得很方便,它不会发生阻塞的状况
比如函数内部的执行需要2S,那setTimeout会等2S执行完以后才去递归调用,也就是说整个一次循环需要3S的时间。
但是setTimeout又不如setInterval执行的精确,所以在不同情况下可以选择不同的定时器以达到最好的效果。
3.如何写出一个高效的倒计时
身为小白的我目前能写出来的只是能实现该需求的最低级的代码OTZ
1 var t,num = 60;
2 function timeDown(){
3 num--;
4 num = num < 10 ? ('0'+num) : (num+''); // 左侧补0操作
5 document.getElementById('demo').innerHTML = num;
6 t = setTimeout(function(){
7 timeDown();
8 },1000);
9 if(num <= 0){
10 clearTimeout(t);
11 }
12 }
13 timeDown();
以上代码是用setTimeout递归调用外层函数实现的
下面是用setInterval实现的方法:
1 var t,num = 5;
2 function timeDown(){
3 num--;
4 num = num < 10 ? ('0'+num) : (num+'');
5 document.getElementById('demo').innerHTML = num;
6 if(num <= 0){
7 window.clearInterval(t);
8 }
9 }
10 t = setInterval(timeDown,1000);
以上用两种定时器写出的代码都是运用的最基本的实现方式,也是我现在的经验和水平能写出来的,
前段时间在掘金社区看到一个很厉害的由浅入深的写倒计时的方法,现在粘贴过来跟我的对比一下,简直无地自容OTZ
首先是左侧补0的几种方法:
//方法一:大于10的返回值的类型是number类型,没有做转换 function leftPad(i){ if(i<10){ i = "0" + i; } return i; } //方法二:对上一种方法做了弥补,但是没有考虑到复数的情况 function leftPad(i){ return i < 10 ? '0'+i : i+''; } //方法三:对返回值类型做了转换,同时也考虑到了负数的情况,这也是目前我的水平能想到的写法了。。 function leftPad(n){ var n = parseInt(n, 10); return n > 0 ? n <= 9 ? ('0'+n) : (n+'') :'00'; } //下面两种方法我其实就有点看不懂了,不过还是先贴在这里,等我学会了再回来看看 function leftPad(n, len){ len = len || 2; n = n + ''; var diff = len - n.length; if (diff > 0) { n = new Array(diff + 1).join('0') + n; } return n; } function leftpad (str, len, ch) { str = String(str); var i = -1; if (!ch && ch !== 0) ch = ' '; len = len - str.length; while (++i < len) { str = ch + str; } return str; }
然后是倒计时的代码:
方法一:
单例模式,简单方便好理解,缺点是每次init都会拿一个新定时器,性能不好。继承和扩展能力一般,无法获取实例属性,导致了执行状态都是不可见的。
var CountDown = { $ : function(id){/*id选择器*/}, init :function(startTime,endTime,el){/*执行定时器入口,使用setTimeout调用_timer*/}, _timer : function(startTime,endTime,el){/*私有方法,处理时间参数等具体业务*/} } CountDown.init("","2016,04,23 9:34:44","countdown1");
方法二:
标准的原型构造器写法,简单方便好理解,确定是每次都拿一个新定时器,实例增多后性能同样不好,按道理setTime,leftPad等方法都可以通过继承来实现,方便扩展和复用,
prototype上的方法均为辅助方法,按理不应该被外部调用,这里应该封装为私有方法或者前缀+_,优点可以通过实例拿到相关倒计时属性,可以对实例再做扩展操作。
function Countdown(elem, startTime, endTime) { this.elem = elem; this.startTime = (new Date(startTime).getTime()) ? (new Date(startTime).getTime()) : (new Date().getTime()); this.endTime = new Date(endTime).getTime(); } Countdown.prototype = { SetTime: function() {}, leftPad: function(n) {}, DownTime: function() {} } var test = new Countdown("time", "2016/1/30,12:20:12", "2017/1/30,12:20:12"); test.SetTime();
方法三:
优点:这里的countdown是一个比较简单的工厂模式实现,实现了一个统一的create方法,create方法上调用了style这个属性上扩展的样式(style1-3)实现,
create方法返回的是一个独立的新实例,并统一扩展了go方法,go方法里统一创建定时器并挂载到timer属性,在这里我们也就等同拥有了修改和控制每个工厂造出来的单例的能力,
样式做到了可扩展,leftPad,timeToSecond也可以方便通过一个utils对象来进行继承。
缺点:没有考虑到上面提到的setTimeout和setInterval的区别,也没有时间校验机制,在性能方面考虑不多。
var countdown = {}; countdown.leftPad = function(n, len) {}; countdown.timeToSecond = function(t) {}; /** * 倒计时工厂 * @param {[object]} obj 倒计时配置信息 * @return {[object]} 返回一个倒计时对象 */ countdown.create = function(obj) { var o = {}; o.dom = document.getElementById(obj.id); o.startMS = +new Date(obj.startTime || 0); o.endMS = +new Date(obj.endTime || 0); obj.totalTime && (o.totalTime = countdown.timeToSecond(obj.totalTime)); var newCountdown = new countdown.style[obj.style](o); newCountdown.go = function(callback) { callback && (newCountdown.callback = callback); newCountdown.render(); clearInterval(newCountdown.timer); newCountdown.timer = setInterval(newCountdown.render, 1000); }; return newCountdown; }; countdown.style.style1 = function(obj) { this.dom = obj.dom; this.startMS = obj.startMS; this.endMS = obj.endMS; var _this = this; this.render = function() { var currentMS = +new Date(); var diff = (_this.endMS - currentMS) / 1000; var d = parseInt(diff / 60 / 60 / 24); d = countdown.leftPad(d, 3); d = d.replace(/(\d)/g, '<span>$1</span>'); _this.dom.innerHTML = '距离国庆节还有:' + d + '天'; if (currentMS > _this.endMS) { clearInterval(_this.timer); if (_this.callback) { _this.callback(); } else { _this.dom.innerHTML = '国庆节倒计时结束'; } } }; }; countdown.style.style2 = function(obj) {}; countdown.style.style3 = function(obj) {}; countdown.create({id:"clock3",totalTime:'82:23',style:'style1'}).go(function(){alert('It is over');});
以上是我在社区里看到的前几个例子,后面的几个例子已经是我看不懂的阶段了。。。所以就不贴在这里了,先保存记录,然后再慢慢学习
自己的程序员之路还有很远要走。。。
by新手小白的纪录