函数防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。
当然,需要判断在触发事件时,是否需要立刻执行回调函数。
/**
 * @param fn 回调函数
 * @param delay 延迟执行毫秒数
 * @param immediate true 指立即执行回调,false 指非立即执行回调
 */
function debounce(fn, delay, immediate) {
    let timer = null;
    return function () {
        let context = this;
        let args = arguments;
        // 如果此时存在定时器,则取消之前并重新计时
        if (timer) {
            clearTimeout(timer);
            timer = null;
        }
        
        if (immediate) {
            let _timer = !timer;
            timer = setTimeout(function() {
                timer = null;
            }, delay);
            
            if (_timer) fn.apply(context, args);
        }
        else {
            timer = setTimeout(function() {
                fn.apply(context, args);
            }, delay);
        }
    }
}
// 调用防抖函数
// foo 回调函数
document.getElementById('xxx').onclick = debounce(foo, 500, true);
函数节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll、resize、mousemove等函数的事件监听上,通过事件节流来降低事件调用的频率。
同样,函数节流需要判断触发事件时,是否需要立刻执行回调函数。
/**
 * @param fn 回调函数
 * @param delay 延迟执行毫秒数
 * @param immediate true 指立即执行回调,false 指非立即执行回调
 */
function throttle(fn, delay, immediate) {
    let preTime = 0;
    let timer = null;
    return function () {
        let context = this;
        let args = arguments;
        let nowTime = Date.now();
        if (immediate) {
            if (nowTime - preTime >= delay) {
                preTime = nowTime;
                fn.apply(context, args);
            }
        } else {
            if(!timer) {
                timer = setTimeout(function() {
                    timer = null;
                    fn.apply(context, args);
                }, delay);
            }
        }
    };
}
// 调用节流函数
// foo 回调函数
document.getElementById('xxx').onmousemove = debounce(foo, 500, true);