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