JavaScript中的時間是通過定時器控制的,他們分別是window.setInterval和window.setTimeout,我們當然可以省略window,直接使用方法名稱調用。 一 setTimeout 在等待指定的毫秒數後執行函數,語法如下: setTimeout(code/functio ...
JavaScript中的時間是通過定時器控制的,他們分別是window.setInterval和window.setTimeout,我們當然可以省略window,直接使用方法名稱調用。
一 setTimeout
在等待指定的毫秒數後執行函數,語法如下:
setTimeout(code/function, milliseconds, param1, param2, ...)
方法接受2個或多個參數,第一個是一段JS代碼或一個函數引用,第二個是需要等待的時間(以毫秒計),如果第一個參數是函數引用,並且需要傳遞參數,可以在後面依次傳入。方法返回一個唯一id,代表該定時器,使用clearTimeout(id)可以清除定時器。
1 setTimeout(functon(){ 2 console.log(new Date()); 3 },3000); 4 console.log(new Date()); 5 //立即顯示一次當前時間,3秒後又將顯示一次
經測試發現:如果第一個參數是一段JS代碼而非函數引用,該代碼將立即被執行,而不會延時等待。
二 setInterval
等同於 setTimeout(),但持續重覆執行該函數。語法如下:
setInterval(code/function, milliseconds, param1, param2, ...)
使用方法和setTimeout()相同。
1 function timer(){ 2 var d = new Date(); 3 document.body.innerText = d; 4 } 5 setInterval(timer,1000);
上面是一個簡單的定時器示例。
另外,setInterval方法有一個嚴重的缺陷,那就是不能確保執行時間的準確性。
在執行setInterval()時,假如我們設置每1s執行一次函數,但函數執行一次需要花費2s。瀏覽器會每隔1s就向一個事件隊列中添加一個事件(即執行一次函數),當第一次執行完畢(3s之後了),這時隊列中已經有2個事件正在排隊了,於是瀏覽器立即執行第二個事件(等待隊列中的第一個)。這樣明顯和我們本意每1s執行一次函數不符。所以在setInterval中,與其說第二個參數是延時時間,不如說是每個事件執行的最大間隔時間更為準確。因為當事件執行時間大於設置的間隔時間時,兩個任務執行之間是沒有間隔時間的。
有些書上表示,到了設置的時間點,如果上一次的函數還沒執行完,那麼本次事件將不會被添加到事件隊列中去,這次事件將被跳過,直到未來設置的某一時間點,前面的任務已經完成,才向隊列中添加下一個事件。但瀏覽器實際上是按照第一種方式管理事件隊列的,即到了時間不管前面的是否執行完畢,都插入一個進去,然後依次等待執行。(我用的Chrome 76.0.3869.100測試)
setInterval主要應用在繪製動畫效果上,鑒於它對時間的不准確性,要想獲得完美的動畫效果請使用CSS3的Animation實現。另外,如果確實需要使用,請務必把握好間隔時間的設置。下麵是使用setInterval封裝的一個運動函數:
1 var timer; 2 function startMove(obj,json,Fn){ 3 var key; 4 clearInterval(timer); 5 timer = setInterval(function(){ 6 for(var prop in json){//json是一個包含需要改變的屬性和目標值的對象。 7 var bridge; 8 if(prop == 'opacity'){ 9 bridge = getStyle(obj,prop)*100;//為了便於計算,先放大100倍 10 }else{ 11 bridge = parseInt(getStyle(obj,prop)); 12 } 13 var speed = (json[prop] - bridge)/6; 14 speed = speed > 0?Math.ceil(speed):Math.floor(speed);//這裡為了獲得更好的動畫效果,動態設置了運動速度。你當然可以給一個常數作為運動速度 15 if(prop == 'opacity'){ 16 obj.style[prop] = (bridge + speed) / 100; 17 }else{ 18 obj.style[prop]= bridge + speed + 'px'; 19 } 20 if(bridge != json[prop]){ 21 key = false; 22 }else{ 23 key = true; 24 } 25 if(key){ 26 clearInterval(timer); 27 if(Fn)Fn();//當目標運動完成,執行回調函數 28 } 29 } 30 },30); 31 } 32 33 function getStyle(obj,prop){//獲取css樣式值 34 if(obj.currentStyle){//IE 35 return obj.currentStyle(prop); 36 }else{//其他 37 return getComputedStyle(obj,false)[prop]; 38 } 39 }
網上很多文章亦或是很多出版書籍都把這兩個方法和DOM放在一起或者單獨講解,我想他們這麼做的原因大概是這兩個方法在JavaScript中主要應用在實現DOM元素的動畫效果上。那麼我為什麼要把他們放在BOM裡面講呢?
我們知道,JS的執行是單線程的,當代碼運行到這兩個方法時,理論上應該等待它綁定的代碼執行完再往後執行其他代碼,實際上他們並不會阻塞後面的代碼,這又是為什麼呢?原來每當遇到延時程式時,瀏覽器都會為它單獨開啟一個線程,換言之,他們的時間是由瀏覽器操縱的,而不是JavaScript。另一個明顯的理由是這兩個方法本來就是綁定在window對象上的,是由瀏覽器實現的,所以我把它放在BOM講啦!