防抖和节流笔记

307 阅读3分钟

在开发中有些时候我们并不希望在事件持续触发的过程中那么频繁地去执行函数

首先我们简单的设置一下html,给div加上一个鼠标的mousemove事件。

<div id="main"
    style="height:300px;line-height:300px;text-align:center; color: #fff;background-color:#999;font-size:80px;"></div>
    <script>
        let sum = 1;//
        let main = document.getElementById('main');

        function count() {
            main.innerHTML = num++;
        };
        main.onmousemove = count;
    </script>

可是我们发现这个++操作实在太频繁,数字变化太快,如果能让他一段时间里只执行一次 效果就好太多了。

防抖(debounce)

所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

防抖函数分为非立即执行版和立即执行版。

非立即执行版:

function debounce(func, wait) {
    let timeout;
    return function () {
        let _this = this
        let arg = arguments;
        if (timeout)  clearTimeout(timeout) //在触发事件的wait秒内,又触发了事件,此时清除定时器
        timeout=setTimeout(()=>{ //重新计算定时器执行时间
            func.apply(_this,arg)
        },wait)
    }
}
    main.onmousemove = debounce(count, 1000);

非立即执行版的意思是触发事件后函数不会立即执行,而是在 n 秒后执行,如果在 n 秒内又触发了事件,则会重新计算函数执行时间

立即执行版:

function debounce(func, wait) {
    let timeout;
    return function () {
        let _this = this
        let arg = arguments;
        if (timeout)  clearTimeout(timeout) 
        let callfunc = !timeout
        timeout = setTimeout(() => {
            timeout = null
        }, wait)
        if (callfunc) func.apply(_this, arg)
    }
}
    main.onmousemove = debounce(count, 1000);

立即执行版的意思是触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果。

方法合并:

当然我们也可以将非立即执行版和立即执行版的防抖函数结合起来,实现最终的防抖函数。

    function debounce(func, wait, perform) {
        let timeout;
        return function () {
            let _this = this
            let arg = arguments;
            if (timeout)  clearTimeout(timeout) 
            if (perform) { //perform为truelet callfunc = !timeout
                timeout = setTimeout(() => {
                    timeout = null
                }, wait)
                if (callfunc) func.apply(_this, arg)
            }else{
                timeout=setTimeout(()=>{
                    func.apply(_this,arg)
                },wait)
            }

        }
    }
    main.onmousemove = debounce(count, 1000,false);

节流

所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。

对于节流,一般有两种方式可以实现,分别是时间戳版和定时器版。

时间戳版:

function throttle(func, wait) {
    let previous = 0;
    return function() {
        let now = Date.now();
        let _this = this;
        let args = arguments;
        if (now - previous > wait) {
            func.apply(_this, args);
            previous = now;
        }
    }
}

使用方式

main.onmousemove = throttle(count,1000);

定时器版:

function throttle(func, wait) {
    let timeout;
    return function () {
        let _this = this;
        let args = arguments;
        if (!timeout) {
            timeout = setTimeout(() => {
                timeout = null;
                func.apply(_this, args)
            }, wait)
        }

    }
}

使用方式

main.onmousemove = throttle(count,1000);

两种版本比较,我们发现时间戳版会在第一次触发时立即执行函数,而定时器版会等待定时器事件结束后执行第一次。

同样我们也可以合并起来一起写

function throttle(func, wait, type) {
        if (type === 1) {
            var previous = 0;  //注意在if块级作用域中不能使用let声明变量 否则会报错
        } else if (type === 2) {
            var timeout;
        }
        return function () {
            let _this = this;
            let args = arguments;
            if (type === 1) {
                let now = Date.now();

                if (now - previous > wait) {
                    func.apply(_this, args);
                    previous = now;
                }
            } else if (type === 2) {
                if (!timeout) {
                    timeout = setTimeout(() => {
                        timeout = null;
                        func.apply(_this, args)
                    }, wait)
                }
            }
        }
    }

总结

防抖可以理解为事件触发后一个函数开时执行需要n秒的时间,如果在这n秒内再次触发事件,则会重新计算第n秒后才开始执行。

节流可以理解为事件触发后一个函数开始执行需要n秒的时间,如果在这n秒内再次触发事件都会无效,直到n秒后函数执行完毕事件才会生效。