JS中函式是一等公民,地位很高。本文介紹快速介紹三個函式:
throttle
節流函式debounce
去抖函式compose
組合函式,這函式就是把一系列函式按順序一個個執行。
節流和去抖函式
當某些事件頻繁觸發造成不斷執行DOM操作或資源載入等行為,這時你可能就會想到用節流和去抖函式來處理。
throttle
節流函式
節流函式就是預先設定一個執行週期,當呼叫動作的時刻大於等於執行週期則執行該動作,然後進入下一個新週期。
可以用在下面兩個情景中:
- window物件的resize、scroll事件
- 拖拽時的mousemove事件
簡單實現
function throttle (fn, delay) {
let last = 0
return (...args) => {
const now = Date.now()
if (now - last > delay) {
fn.call(this, args)
last = now
}
}
}
複製程式碼
debounce
去抖函式
去抖函式就是呼叫動作n毫秒後,才會執行該動作,若在這n毫秒內又呼叫此動作則將重新計算執行時間。
通常用在下面的場景中:
- 文字輸入、自動完成的keyup事件
簡單實現
function debounce (fn, delay) {
let last = 0
return (...args) => {
clearTimeout(last)
last = setTimeout(() => {
fn.call(this, args)
}, delay)
}
}
複製程式碼
這邊找了一個網上的實現版本:
/*
* 頻率控制 返回函式連續呼叫時,fn 執行頻率限定為每多少時間執行一次
* @param fn {function} 需要呼叫的函式
* @param delay {number} 延遲時間,單位毫秒
* @param immediate {bool} 給 immediate引數傳遞false 繫結的函式先執行,而不是delay後後執行。
* @return {function}實際呼叫函式
*/
var throttle = function (fn,delay, immediate, debounce) {
var curr = +new Date(),//當前事件
last_call = 0,
last_exec = 0,
timer = null,
diff, //時間差
context,//上下文
args,
exec = function () {
last_exec = curr;
fn.apply(context, args);
};
return function () {
curr= +new Date();
context = this,
args = arguments,
diff = curr - (debounce ? last_call : last_exec) - delay;
clearTimeout(timer);
if (debounce) {
if (immediate) {
timer = setTimeout(exec, delay);
} else if (diff >= 0) {
exec();
}
} else {
if (diff >= 0) {
exec();
} else if (immediate) {
timer = setTimeout(exec, -diff);
}
}
last_call = curr;
}
};
/*
* 空閒控制 返回函式連續呼叫時,空閒時間必須大於或等於 delay,fn 才會執行
* @param fn {function} 要呼叫的函式
* @param delay {number} 空閒時間
* @param immediate {bool} 給 immediate引數傳遞false 繫結的函式先執行,而不是delay後後執行。
* @return {function}實際呼叫函式
*/
var debounce = function (fn, delay, immediate) {
return throttle(fn, delay, immediate, true);
};
複製程式碼
自己畫了一張圖便於理解,最左邊的boolean
值是上邊的immediate
值,沿x軸的線是時間線,時間線上面是使用者觸發的動作,下面是函式的執行。
上面函式有點奇怪的地方就是immediate
引數傳遞false
繫結的函式先執行。
先看debounce
在immediate
不同的情況下的表現:
在immediate
為false
的情況下,第一次觸發動作,函式就馬上被執行了,後續的在delay時間段內重複按鍵都不會觸發函式,而且會重置delay開始計算的時間點。當觸發動作間隔時間超過delay後,又回到了初始狀態,一觸發動作就執行函式。
在immediate
為true
的情況下,觸發動作後,函式並沒有馬上執行,只有在經過delay時間段內,沒有觸發動作時,函式才會被執行。
接著看throttle
在immediate
不同的情況下的表現:
在immediate
為false
的情況下,第一次觸發動作,函式就馬上被執行了,在這次執行後,在後續的delay時間段內觸發動作都不會執行函式,間隔大於delay後,回到初始狀態,觸發動作函式就執行。
在immediate
為true
的情況下,第一次觸發動作,函式就馬上被執行了,同時定時器設定延時delay時間後在執行函式。其實和上面情況差不多,只不過會在delay後多執行一次。當然還有一些細節不同,圖中可以看到,有兩個執行之間時間並沒有間隔delay。
compose
函式
這個直接上程式碼了,是從redux那邊借過來的。
/**
* Composes single-argument functions from right to left. The rightmost
* function can take multiple arguments as it provides the signature for
* the resulting composite function.
*
* @param {...Function} funcs The functions to compose.
* @returns {Function} A function obtained by composing the argument functions
* from right to left. For example, compose(f, g, h) is identical to doing
* (...args) => f(g(h(...args))).
*/
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
複製程式碼
核心程式碼就一行,沒錯,就一行。
沒看到之前,我可能會通過for
迴圈來做,上面程式碼用到陣列方法reduce
,在配合剩餘引數語法和擴充套件運算子...
來傳參就可以實現函式組合的功能,非常精簡。
介紹到這裡就結束了,後續碰到到驚豔的函式還會繼續分享。