1.setTimeout和setInterval都屬於js中的定時器,能夠規定延遲時間再執行某個操做,不一樣的是setTimeout在規定時間後執行完某個操做就中止了,而setInterval則能夠一直循環執行下去。程序員
下面介紹一下兩種定時器的語法:express
setTimeout(expression,milliseconds) 以及 setInterval(expression,milliseconds)dom
其中 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);學習
可是有些場合會要求必須使用函數名做爲調用句柄,此時能夠用另外一種方法帶參數:this
方法一:spa
function fun(name){ alert('hello'+' ' +name); } function _fun(name){ return function(){ fun(name); } } setTimeout(_fun('Tom'),1000); //參數是函數名
方法二:prototype
不用單獨再定義一個函數,直接將函數調用放在一個函數裏面,可使用函數名做爲調用句柄:線程
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新手小白的紀錄