JS函数防抖与节流

WEB前端 244 2018-10-24 16:54

函数节流和防抖在前端开发中应用广泛,例如:函数防抖可以用于对用户输入的自动补全操作、函数节流可以用于对用户浏览行为的捕捉。

防抖

函数在事件触发 n 秒后才执行,如果一个事件触发的 n 秒内又触发了这个事件,那就以新的事件的触发时间为准

基本版
function denounce(func, wait) {
    var timeout;
    return function (){
        clearTimeout(timeout);
        setTimeout(func, wait);
    }
}
解决 setTimeout 执行过程中 this 指向问题
function debounce (func, wait){
    var timeout
    return function (){
        var that = this
        clearTimeout(timeout)
        timeout = setTimeout(function(){
            func.apply(that)
        }, wait)
    }
}
解决 event 参数传递
function debounce (func, wait){
    var timeout
    return function (){
        var args = arguments
        var that = this
        clearTimeout(timeout)
        timeout = setTimeout(function(){
            func.apply(that, args)
        }, wait)
    }
}
加上可立即执行判断
function debounce (func, wait, immediate){
    var timeout
    return function (){
        var args = arguments
        var that = this
        clearTimeout(timeout)
        if (immediate){
            if (!timeout){
                func.apply(that, args)
            }
            timeout = setTimeout(function(){
                timeout = null
            }, wait)
        } else {
            timeout = setTimeout(function(){
                func.apply(that, args)
            }, wait)
        }
    }
}
加上返回值
function debounce (func, wait, immediate){
    var timeout, result
    return function (){
        var args = arguments
        var that = this
        clearTimeout(timeout)
        if (immediate){
            if (timeout){
                result = func.apply(that, args)
            }
            timeout = setTimeout(function(){
                timeout = null
            }, wait)
        } else {
            timeout = setTimeout(function(){
                func.apply(that, args)
            }, wait)
        }
        return result
    }
}
增加取消上次防抖等待
function debounce (func, wait, immediate){
    var timeout, result
    var debounced = function (){
        var args = arguments
        var that = this
        clearTimeout(timeout)
        if (immediate){
            if (!timeout){
                result = func.apply(that, args)
            }
            timeout = setTimeout(function(){
                timeout = null
            }, wait)
        } else {
            timeout = setTimeout(function(){
                func.apply(that, args)
            }, wait)
        }
        return result
    }
    debounced.cancel = function(){
        clearTimeout(timeout)
        timeout = null
    }
    return debounced    
}

节流

函数在每个规定时间间隔内只会被触发一次

两种基本方式
时间戳版(效果:第一次触发马上执行,最后一次触发不会有效果)
function throttle(func, wait) {
    var pre = 0
    return function(){
        var now = Date.now()
        var that = this
        var args = arguments
        if (now - pre > wait) {
            func.apply(that, args)
            pre = now
        }
    }
}
定时器版(效果:第一次触发延迟执行,最后一次触发有效果)
function throttle(func, wait){
    var timeout
    return function(){
        var that = this
        var args = arguments
        if (!timeout) {
            timeout = setTimeout(function (){
                timeout = null;
                func.apply(that, args)
            }, wait)
        }
    }
}
结合可配置版
function throttle(func, wait, options) {
    var timeout, context, args, result;
    var previous = 0;
    if (!options) options = {};

    var later = function() {
        previous = options.leading === false ? 0 : new Date().getTime();
        timeout = null;
        func.apply(context, args);
        if (!timeout) context = args = null;
    };

    var throttled = function() {
        var now = new Date().getTime();
        if (!previous && options.leading === false) previous = now;
        var remaining = wait - (now - previous);
        context = this;
        args = arguments;
        if (remaining <= 0 || remaining > wait) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            previous = now;
            func.apply(context, args);
            if (!timeout) context = args = null;
        } else if (!timeout && options.trailing !== false) {
            timeout = setTimeout(later, remaining);
        }
    };
    return throttled;
}
文章评论