摘要:而寫成還可以滿足你獲得回調函數返回值的需求。而構建函數表達式的方法也不止把聲明括起來這種,一些其他的操作符也可以,比如賦值號到目前為止,我們似乎能夠得出結論函數聲明后不可直接跟圓括號,而函數表達式后面可以。
使用setTimeout替代setInterval
setInterval()這個間歇調用函數是應用得比較廣的,尤其在比較古老的瀏覽器中實現動畫效果時,往往離不開它。然而這個函數卻有不少坑,由于其實現是把要執行的代碼插入待執行隊列排隊執行,同時為防止連續執行,這個隊列中只能有一個最早進來的它的代碼實例。如果隊列中也有其他任務在等待,而且執行了很長時間,首先就很容易導致計時不準;再者,還會打亂其執行的時間線,導致setInterval()遲遲不能再添加新的代碼實例,最終出現略過某次執行的現象等問題,如下圖所示:
如果你要獲取每次間歇執行的結果,那就要避免setInterval()可能略過某次執行缺點,可以用setTimeout()改寫它:
//原函數 var intervalId = setInterval(function () { if(someCondition) { clearInterval(intervalId); console.log("done"); } }, 1000); //改寫后 function fun() { if(someCondition) { console.log("done"); } else { setTimeout(fun, 1000); } } setTimeout(fun, 1000);
雖然setTimeout()的計時也未必很準確,但由于上述代碼是鏈式使用的,一環扣一環,從源頭阻止了略過某次執行的情況。同時使用setTimeout()也更靈活些、定制性更強,想執行到某個周期就停下來,只要你不繼續調用就可以了,沒必要專門再加個clearTimeout()。最后,既然是鏈式執行,那你第一個調用可用普通的函數調用即可,然后在執行時自然會調用setTimeout(),而不用像setInterval()那樣第一個調用也必須等待一段時間,這樣可以滿足一些更細致的要求。
如何給setTimeout()的回調函數傳參?這個問題其實也代表了一類問題,即如何在如setTimeout()、setInterval()、指定DOM事件等限制使用函數引用而非函數調用的場合,仍然能給將來要調用的函數傳遞參數。
為什么要給函數傳參?因為傳參和返回值一樣,是函數作為子程序與外界通信的一大手段。但在上面提到那些限制必須使用函數引用的場景下,如果我們只用函數的引用而沒有調用函數,那就意味著不能給直接給它傳參、也不能接受它的返回值,這就使得子程序間的數據通信少了一大功能,此時,你要讓回調函數與外界通信貌似也就只有通過全局變量了。
但深入想一想,這些場景只是規定要用到函數引用,并沒有說一定是回調函數的引用,那也意味著我們可以偷梁換柱,用別的函數引用來“占著”本來應該是回調函數引用的位置,而讓回調函數進行調用。于是我們就有了解決方法,以setTimeout()為例最簡單的一種想法如下:
setTimeout(function() { callback(arg); }, timeout);
這里用到一個匿名函數作為第一個參數,匿名函數提供了它的函數引用后,回調函數就可以在匿名函數體內直接調用、傳參了。而寫成return callback(arg)還可以滿足你獲得回調函數返回值的需求。
除了外套匿名函數這種方法,我們還可以使用功能更加強大的閉包,來解決這個傳參問題:
setTimeout((function(arg) { return function () { callback(arg); }; })(arg), timeout);
其實就是在第一種方法的基礎上再外套一個立即執行表達式,這個閉包實際上是包含了傳參操作的,但有了它我們就可以來解決那些我們通過構造閉包可以解決的的問題了。比如獲取某次循環等更具體的塊級作用域中的變量值:
for (i in Arr) { setTimeout((function(arg) { return function () { callback(arg); }; })(i), timeout); }函數聲明和函數表達式
在使用立即執行表達式(IIFE)時,我們常采用(function () {})()這種形式,若去掉第一組括號直接在函數聲明后面加括號則會報錯:
function () {}() //Uncaught SyntaxError: Unexpected token (
顯然js的函數聲明后面是不能直接加括號讓它立即執行的,但我們把函數聲明括起來,立即執行的就不是一個函數,而是一個所謂函數表達式了。而構建函數表達式的方法也不止把聲明括起來這種,一些其他的操作符也可以,比如賦值號:
var A = function () {}();
到目前為止,我們似乎能夠得出結論:函數聲明后不可直接跟圓括號,而函數表達式后面可以。然而js認為什么是函數聲明、什么是函數表達式卻并不是簡單靠函數聲明的外在形式來決定的,比如下面這個例子:
function A(i){ console.log(i); } A(function () { return 5; }()); //5
上面的代碼里A函數的參數直接在函數聲明后面加上了括號,看起來是個非法的IIFE了,而這個“非法”IIFE作為參數時卻能正常地立即執行。但再仔細想想,我們知道傳參的過程其實是相當于賦值的,所以這種傳參形式其實和上面提到的通過賦值號構建函數表達式應該是一個道理,立即執行的其實還是是函數表達式,而非函數聲明,不要被表像迷惑了。
而為防止以后讀代碼時再踩坑,我想出了個更簡單的區分兩者的“方法”:只要function不是放在一個語句最前面時就可以連同后面的聲明內容當作函數表達式,就可以加括號立即執行。
當然在寫代碼用到IIFE時,就沒必要糾結這些降低效率了,直接在聲明外括括號得了,這樣既寫起來方便又看起來簡潔明了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/78760.html
閱讀 2356·2021-11-25 09:43
閱讀 2867·2021-11-24 09:39
閱讀 2933·2019-08-30 11:10
閱讀 1139·2019-08-29 16:34
閱讀 603·2019-08-29 13:25
閱讀 3363·2019-08-29 11:21
閱讀 2865·2019-08-26 11:39
閱讀 2398·2019-08-26 11:34