settimeout()和setinterval()的區別,它們之間的相互模擬;以及避免雙重求值 ...
前幾天翻書,看到“避免雙重求值”一節時有提到settimeout()、setinterval() 建議傳入函數而不是字元串以作為第一個參數,所以這裡總結一下settimeout()和setinterval()的區別,以及它們之間的相互模擬。
setTimeout(): 方法用於在指定的毫秒數後調用函數或計算表達式(函數更好,下麵會解釋為什麼函數更好!)。
語法:setTimeout(code,millisec) code:必需,要調用的函數後要執行的 JavaScript 代碼串;millisec:必需,在執行代碼前需等待的毫秒數。
setInterval() :方法用於按照指定的周期(以毫秒計)來迴圈調用函數或計算表達式,直到 clearInterval() 被調用或視窗關閉,由 setInterval() 返回的 ID 值可用作 clearInterval() 方法的參數。
語法:setInterval(code,millisec[,"lang"]) code:必需,要調用的函數或要執行的JavaScript 代碼串;millisec:必須,周期性執行或調用 code 之間的時間間隔,以毫秒計。
從上面的定義可以看出,setTimeout(表達式,延時時間)在執行時,是在載入後延遲指定時間後,去執行一次表達式,次數是一次;而 setInterval(表達式,時間間隔)則不一樣,它從載入後,每隔指定的時間就執行一次表達式,只要視窗不關閉或 clearInterval() 調用就會無限迴圈下去。所以,兩者是完全是不一樣的,具體看下麵測試代碼和結果!
var intervalNum = 0, timeoutNum =0; function testsetInterval(){ var date = new Date(); console.log(date.getSeconds()); console.log("setInterval", intervalNum++); } function testsetTimeout(){ var date = new Date(); console.log(date.getSeconds()); console.log("setTimeout", timeoutNum++); } function testFuntion() { setInterval(function () { testsetInterval() },4000); //每4秒執行testsetInterval()一次 setTimeout(function () { testsetTimeout() },10000); //延遲10秒執行testsetTimeout()一次,只執行一次;單獨的setTimeout()方法,需要有另外的方法去觸發,如將其放在 body 的 onload事件方法內 }
從圖中的結果可以看出,setInterval() 每4秒迴圈執行一次;然而setTimeout()在延遲10秒(37+10),執行一次後,再沒執行!
雖然兩者不一樣,但是卻可以相互模擬。具體使用那個,以具體的需求和場景具體分析,就像for迴圈可以模擬所有的迴圈一樣(包括分支,以及do while一樣)。一般情況下 setTimeout() 用於延遲執行某方法或功能;setInterval() 則一般用於刷新表單,對於一些表單的假實時指定時間刷新同步。
模擬 setInterval() :將 setTimeout() 包含在一個執行函數A中,而setTimeout() 自己的code執行函數又是A,然後在函數A外將函數A執行一次,即達到了迴圈執行的目的。
var intervalNum = 0; function testsetInterval() { var date = new Date(); console.log(date.getSeconds()); console.log("setInterval", intervalNum++); } function recursive() { testsetInterval(); setTimeout(function () { recursive() //遞歸,每隔4秒調用一次recursive() }, 4000) } function testFuntion() { recursive(); //在方法recursive外,調用一次recursive,以啟動迴圈調用! }
迴圈執行,和setInterval()功能相同
模擬 setTimeout() :用 setInterval() 模擬 setTimeout() 很簡單,在 setInterval() 執行一次後,立刻關閉視窗(當然這是耍無賴)或者執行 clearInterval() 方法(這個靠譜點)。clearInterval() 需要在 setInterval()執行code方法內或其他地方執行,不能緊接著 setInterval() 後面執行,那樣setInterval() 還沒等到執行,就已經被幹掉了。
var intervalNum = 0, clearId = 0; function testsetInterval(){ var date = new Date(); console.log(date.getSeconds()); console.log("setInterval", intervalNum++); clearInterval(clearId); //也可以在此執行 } function testFuntion() { clearId = setInterval(function () { testsetInterval(); //每隔4秒調用testsetInterval() // clearInterval(clearId); //可以在此執行 },4000); }
執行一次,關閉 setInterval(),和 setTimeout() 功能相同
最後,將書中看到的“避免雙重求值”搬到這。以解釋為什麼 “ 建議傳入函數而不是字元串以作為第一個參數”。
setTimeout()、setInterval() 允許傳入一個JS代碼字元串並執行,然而在JS代碼中執行另一段JS代碼時,代碼首先會以正常的方式求值,然後在執行過程中對包含於字元串中的代碼發起另一個求值運算,從而造成雙重求值。它比直接包含的代碼執行速度慢很多,原因在於, 每次調用setTimeout()、setInterval()都會創建一個新的解釋器/編譯器實例。這必然使得代碼執行速度變慢,效率降低,從而造成性能的浪費。所以建議傳入函數而不是字元串來作為第一個參數。
我做了一個小測試。從 0 自加到 1億,比較兩種方式各自的實際耗時,代碼以及測試結果如下:
var Timer ={ //從書上copy的一個JS代碼時間分析對象 _data : {}, start:function (key) { Timer._data[key] = new Date(); }, stop:function (key) { var time = Timer._data[key]; if(time){ Timer._data[key] = new Date()-time; } }, getTime:function (key) { // return Timer._data[key]; console.log("time = "+ Timer._data[key]); } }; var intervalNum = 100000000, clearId = 100000000; function testsetInterval(){ //計算從0 加到 1億,以 傳入函數方式 執行 var temp = 0; while(intervalNum--){ if(temp !== 0){ temp = temp + intervalNum; }else { temp = (intervalNum+1) + intervalNum; } } console.log(temp); Timer.stop("testsetInterval"); //調用stop(),計算時間差 Timer.getTime("testsetInterval"); //將時間差值列印出來 } function testsetTimeout(){ //計算從0 加到 1億,以 字元串方式 執行 var temp = 0; while(clearId--){ if(temp !== 0){ temp = temp + clearId; }else { temp = (clearId+1) + clearId; } } console.log(temp); Timer.stop("setTimeout"); //調用stop(),計算時間差 Timer.getTime("setTimeout"); //將時間差值列印出來 } function testFuntion() { Timer.start("testsetInterval"); //獲取代碼執行前的初始時間 setTimeout(function () { testsetInterval(); //每隔1秒調用testsetInterval() },1000); Timer.start("setTimeout"); setTimeout("testsetTimeout()",1000); //雙重求值模式,每隔1秒調用testsetTimeout() }
,隨著數據量的攀升,耗時的差距,更明顯。下麵以 0 自加到 10億,再試下
看得出,“雙重求值”對性能影響還是蠻大的。雖然現在的CPU主頻都相當高,處理數據相當快,而平時前端處理數據的數量級,也不見得能達到這麼高,但是養成一種好的編程習慣和塑造提高代碼性能的思想總沒得錯!