Skip to content

防抖和节流 #2

Open
Open
@TieMuZhen

Description

@TieMuZhen

一、防抖节流异同比较

相同点:

  • 都可以通过使用setTimeout实现。
  • 目的都是,降低回调执行频率。节省计算资源。

不同点:

  • 函数防抖:在一段连续操作结束后,处理回调,利用clearTimeoutsetTimeout实现。函数节流:在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能。
  • 函数防抖关注一定时间连续触发的事件只在最后执行一次,而函数节流侧重于一段时间内只执行一次(如技能冷却)。

二、防抖函数实现

常用版

/**
 * delay 延迟多久后执行
 */
function debounce(fn, delay) {
    var timer; // 维护一个 timer
    return function () {
        var _this = this; // 取debounce执行作用域的this
        var args = [...arguments];
        if (timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(function () {
            fn.apply(_this, args); // 用apply指向调用debounce的对象,相当于_this.fn(args);
        }, delay);
    };
}

测试用例:

// test
function testDebounce(e, content) {
    console.log(e, content);
}
var testDebounceFn = debounce(testDebounce, 1000); // 防抖函数
document.onmousemove = function (e) {
    testDebounceFn(e, 'debounce'); // 给防抖函数传参
}

高配版

/**
 * fn 传入的要防抖的函数
 * wait 多久执行的时间阈值
 * immediate 为 true 时,表示函数在每个等待时延的开始被调用。immediate 为 false 时,表示函数在每个等待时延的结束被调用。
 */
function debounce(func, wait, immediate = true) {
    let timeout;
    // 延迟执行函数
    // 因为setTimeout内this为全局,context参数是为了改变setTimeout内部this指向以便需要的时候使用 
    const later = (context, args) => setTimeout(() => {
        timeout = null; // 倒计时结束
        if (!immediate) {
            //执行回调
            func.apply(context, args);
            // 将闭包内变量赋值为null,用于垃圾回收
            context = args = null;
        }
    }, wait);
    let debounced = function () {
        let args = arguments;
        if (!timeout) {
            timeout = later(this, args);
            if (immediate) { // 第一次点击马上执行
                func.apply(this, args);
            }
        } else {
            clearTimeout(timeout);
            //函数在每个等待时延的结束被调用
            timeout = later(this, args);
        }
    }
    //提供在外部清空定时器的方法
    debounced.cancel = function () {
        clearTimeout(timeout);
        timeout= null;
    };
    return debounced;
};

使用

用debounce来包装scroll的回调
const better_scroll = debounce((log) => console.log(log), 1000);
document.addEventListener('scroll', better_scroll("滚动事件防抖"))

防抖的应用场景

  • 搜索框输入查询,如果用户一直在输入中,没有必要不停地调用去请求服务端接口,等用户停止输入的时候,再调用,设置一个合适的时间间隔,有效减轻服务端压力。
  • 表单验证
  • 按钮提交事件。
  • 浏览器窗口缩放,resize事件(如窗口停止改变大小之后重新计算布局)等。

三、节流函数实现

常用版

function throttle(fn, delay) {
    var previous = 0;
    // 使用闭包返回一个函数并且用到闭包函数外面的变量previous
    return function() {
        var now = new Date();
        if(now - previous > delay) {
            fn.apply(this, arguments);
            previous = now;
        }
    }
}

测试用例:

// test
function testThrottle(e, content) {
    console.log(e, content);
}
var testThrottleFn = throttle(testThrottle, 1000); // 节流函数
document.onmousemove = function (e) {
    testThrottleFn(e, 'throttle'); // 给节流函数传参
}

高配版

/**
 * fn 传入的要节流的函数
 * wait 多久执行的时间阈值
 */
function throttle(fn, wait) {
    let timer, pre = 0;
    const throttle = function () {
        const context = this; // 保留调用时的this上下文
        const args = arguments; // 保留调用时传入的参数
        let now = +new Date();
        if (now - pre < wait) {
            clearTimeout(timer);
            timer = setTimeout(() => {
                pre = now;
                fn.apply(context, args);
            }, wait);
        } else {
            pre = now;
            fn.apply(context, args);
        }
    }
    throttle.cancel = function () {
        clearTimeout(timer);
        timer = null;
    }
    return throttle;
}

const fn = function (log) {
    console.log(log);
}
const better_scroll = throttle(fn, 1000);
document.addEventListener("scroll", better_scroll("添加节流"));

节流的应用场景

  • 按钮点击事件
  • 拖拽事件
  • onScoll
  • 计算鼠标移动的距离(mousemove)

参考文献

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions