防抖与节流区别与实现

119 阅读1分钟

定义

  • 节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
  • 防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

一个经典的比喻:

想象每天上班大厦底下的电梯。把电梯完成一次运送,类比为一次函数的执行和响应

假设电梯有两种运行策略 debounce 和 throttle,超时设定为15秒,不考虑容量限制

电梯第一个人进来后,15秒后准时运送一次,这是节流

电梯第一个人进来后,等待15秒。如果过程中又有人进来,15秒等待重新计时,直到15秒后开始运送,这是防抖

节流代码实现

时间戳方法

    function throttled(fn, delay = 1000) {
            let starttime = Date.now()
            return function (...args) {
                let endtime = Date.now();
                if (endtime - starttime >= delay) {
                    fn.apply(this, args);
                    starttime = Date.now();
                }
            }
        }

缺点:可能会造成最后一次事件不会被触发

计时器方法

 function throttled(fn, delay = 1000) {
            let timer = null;
            return function (...args) {
                if (!timer) {
                    timer = setTimeout(() => {
                        fn.apply(this, args);
                        timer = null;
                    }, delay);
                }
            }
        }

缺点:第一次事件不会立即执行

时间戳与计时器混合使用

function throttled(fn, delay = 1000) {
            let timer = null;
            let starttime = Date.now();
            return function (...args) {
                let endtime = Date.now();
                clearTimeout(timer);
                if (endtime - starttime >= delay) {
                    fn.apply(this, args);
                    starttime = endtime;
                } else {
                    timer = setTimeout(() => {
                        fn.apply(this, args);
                    }, delay);
                }
            }
        }

防抖代码实现

非立即执行

function debounce(fn, wait) {
            let timer = null;
            return function (...args) {
                if (timer) {
                    clearTimeout(timer);
                }
                timer = setTimeout(() => {
                    fn.apply(this, args);
                }, wait);

            }

        }

立即执行

function debounce(fn, wait) {
            let timer = null;
            return function (...args) {
                if (timer) {
                    clearTimeout(timer);
                }
                let callNow = !timer;
                timer = setTimeout(() => {
                    timer = null;
                }, wait);
                if (callNow) fn.apply(this, args);
            }

        }