手写一个防抖函数

183 阅读3分钟

手写一个防抖函数

  1. 什么是防抖函数: 防抖函数是指在某个函数,某段时间内,无论触发的多少次,都只执行最后一次,并且每次调用函数都重新开始计时,无论这个函数有没有调用成功,直到时间间隔内,没有调用过函数请求

  2. 原理和实现:利用定时器,函数第一次执行时设定一个定时器,之后调用时发现,已经设定过定时器,就清空之前的定时器,重新设定一个定时器,如果存在没有被清空的定时器,当定时器计时结束后出发函数执行。

    // 实现1
    /**
    * @description 防抖函数
    * @param { Function } fn:  时间到时要执行的回调函数
    * @param { number } wait: 时间间隔 默认值为1000ms 
    */
    
    function debounce(fn, wait = 1000) => {
        // 闭包外层产生一个 定时器id
        let timer = null;
        return function (...args){
            // 再次出发  如果存在定时器,就清除定时器
            if(timer) clearTimeout(timer);
            // 并且重新设置一个定时器,也就是说,如果触发了函数,就会继续延长时间间隔,直到中间没有触发过
            timer = setTimeou(() => {
                fn.apply(this, args)
            }, wait)
        }
    }
    
    // DEMO
    // 执行 debounce 函数返回新函数
    const betterFn = debounce(() => console.log('fn 防抖执行了'), 1000)
    // 停止滑动 1 秒后执行函数 () => console.log('fn 防抖执行了')
    document.addEventListener('scroll', betterFn)
    
    
    
    // es6
    const  debounce = (fn, wait = 1000 ) => {
        let timer = null;
        return (...args) => {
            if(timer) clearTimeout(timer);
            timer = setTimeout(() => {
                fn(...args);
            }, wait)
        }
    }
    // DEMO
    // 执行 debounce 函数返回新函数
    const betterFn = debounce(() => console.log('fn 防抖执行了'), 1000)
    // 停止滑动 1 秒后执行函数 () => console.log('fn 防抖执行了')
    document.addEventListener('scroll', betterFn);
    
    
    
    
    // 上面基本上满足了所有的要求了, 但是还可以优化,比如我第一次调用的时候,需要触发,不要等待
    // 实现2
    /**
    * @description 防抖函数
    * @param { Function } fn:  时间到时要执行的回调函数
    * @param { number } wait: 时间间隔 默认值为1000ms 
    * @param { boolean } immediate: 是否第一次就触发回调函数
    */
    // 实现 2
    // immediate 表示第一次是否立即执行
    function debounce(fn, wait = 50, immediate) {
        let timer = null
        return function(...args) {
            if (timer) clearTimeout(timer)
          
          	// ------ 新增部分 start ------ 
          	// immediate 为 true 表示第一次触发后执行
          	// timer 为空表示首次触发
            if (immediate && !timer) {
                fn.apply(this, args)
            }
          	// ------ 新增部分 end ------ 
          	
            timer = setTimeout(() => {
                fn.apply(this, args)
            }, wait)
        }
    }
    
    // DEMO
    // 执行 debounce 函数返回新函数
    const betterFn = debounce(() => console.log('fn 防抖执行了'), 1000, true)
    // 第一次触发 scroll 执行一次 fn,后续只有在停止滑动 1 秒后才执行函数 fn
    document.addEventListener('scroll', betterFn)
    
    
    
    // es6
    function debounce(fn, wait = 1000, immediate){
        let timer = null;
        return (...args) => {
            if (timer) clearTimeout(timer);
            // 只有第一次的时候,这个值才管用,因为只有第一次的时候, 是没有定时器了
            // 因为就算函数执行之后,这个定时器也是没有被销毁的
            if (immediate && !timer){
                fn(...args)
            }
            
            timer = setTimeout(() => {
                fn(...args)
            }, wait)
        }
    }
    
    // DEMO
    // 执行 debounce 函数返回新函数
    const betterFn = debounce(() => console.log('fn 防抖执行了'), 1000, true)
    // 第一次触发 scroll 执行一次 fn,后续只有在停止滑动 1 秒后才执行函数 fn
    document.addEventListener('scroll', betterFn)