防抖和节流-JavaScript版本

282 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情

前情提要

  • 防抖(debounce):在规定的时间内每次触发一次就重置一次设定的时间,直到在规定的时间内没有再触发,然后执行方法。就像我们每次做的电梯,每进去一个人就会更新电梯门的等待时间,直到在规定的时间内没有人继续进去。
  • 节流(throttle):在规定的时间内只触发一次。继续拿电梯举例,假设电梯一次只能乘坐2人。也就是说无论电梯门外等了多少人,一次只能承载2人,剩下的人只有等电梯送完这两个人才能继续进电梯。

应用场景

防抖

  • 表单中需要根据用户的输入动作做即时搜索功能时
  • 监听浏览器的resize事件
  • 监听用户的上拉加载,下拉刷新的动作时
  • 用于手机号,邮箱验证
  • 防抖函数分为立即执行版和非立即执行版
  • ......

节流

  • 防止鼠标连点造成多次提交
  • 防止监听滚动页面时,懒加载多张图片时被多次触发或者滑到底部自动加载更多被多次触发
  • ......

实现方式

防抖

  • 初版
/**
 * @description 防抖
 * @param { function } 函数
 * @param { number } 等待的时间
 * @param { boolean } 是否立即执行
 * @return { function }
 **/
function debounce(fn, wait=500, immediate) {
    let timer;
    return function() {
        if (timer) clearTimeout(timer);
        if (immediate) {
            let callNow = !timer;
            timer = setTimeout(() => {
                timer = null;
            }, wait);
            if (callNow) fn.call(this, ...arguments)
        }
        timer = setTimeout(() => {
            fn.call(this, ...arguments)
        }, wait)
        
    }
}
  • 进阶(增加主动取消的功能)
/**
 * @description 防抖
 * @param { function } 函数
 * @param { number } 等待的时间
 * @param { boolean } 是否立即执行
 * @return { function }
 **/
function debounce(fn, wait=500, immediate) {
    let timer, result;
    result = function() {
        if (timer) clearTimeout(timer);
        if (immediate) {
            let callNow = !timer;
            timer = setTimeout(() => {
                timer = null;
            }, wait);
            if (callNow) fn.call(this, ...arguments);
        }
        timer = setTimeout(() => {
            fn.call(this, ...arguments);
            timer = null;
        }, wait)
    }
    result.cancel = function () {
        clearTimeout(timer);
        timer = null;
    }
    return result
}

节流

  • 初版
/**
 * @description 节流
 * @param { function } 函数
 * @param { number } 等待的时间
 * @return { function }
 **/
// 定时器版本
function throttle(fn, wait=500) {
    let timer;
    return function() {
        if (timer) return;
        timer = setTimeout(() => {
            fn.call(this, ...arguments);
            timer = null;
        }, wait)
    }
}
// 时间戳版本
function throttle(fn, wait=500) {
    let t = 0; // 每次成功执行后更新时间基准
    return function() {
        let delta = new Date().getTime() - t;
        if (delta > wait) {
            fn.call(this, ...arguments);
            t = new Date().getTime();
        }
    }
}
  • 进阶
/**
 * @description 节流
 * @param { function } 函数
 * @param { number } 等待的时间
 * @param { string } 类型: timestamp | setTimeout
 * @return { function }
 **/
// 定时器版本
function throttle(fn, wait=500, type='timestamp') {
    let timer;
    if (type == 'timestamp') t = 0;
    return function() {
        // 如果type是时间戳,则走时间戳模式
        if (type == 'timestamp') {
            let delta = new Date().getTime() - t;
            if (delta > wait) {
                fn.call(this, ...arguments);
                t = new Date().getTime();
            }
        } else { // 如果type是定时器,则走时间戳模式
            if (timer) return;
            timer = setTimeout(() => {
                fn.call(this, ...arguments);
                timer = null;
            }, wait)
        }
    }
}