js 函数防抖和节流

1,685 阅读3分钟

在前端开发中,我们会遇到一些需要持续触发的事件或者一些持续的请求,但是我们不希望在事件触发时过于频繁的去执行函数。

所以这个时候,就出现了函数防抖和函数节流的解决方案。

下面通过这样一个场景来说明:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>函数防抖和函数节流</title>
</head>
<body>
    <div id="content" style="height:150px;line-height:150px;text-align:center; color: #ff1e1e;background-color:#3cccc7;font-size:80px;"></div>
    <script type="text/javascript">
        let num = 1;
        let content = document.getElementById('content');

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

在上述代码中,div 元素绑定了 mousemove 事件,当鼠标在div区域中移动的时候会持续地去触发该事件导致频繁执行函数。

可以看到,在没有通过其它操作的情况下,函数被频繁地执行导致页面上数据变化特别快。所以,接下来让我们来看看防抖和节流是如何去解决这个问题的。

函数防抖(debounce)

函数防抖就是在触发事件后 N 秒内函数只能执行一次,如果在 N 秒内又触发了事件,则重新开始计算函数执行时间

函数防抖分为立即执行和非立即执行两种

  • 函数防抖--非立即执行
//非立即执行
function debounce(fun, delay) {
    let timeout;    //需要一个外部变量,为增强封装,所以使用闭包
    return function () {
        let _this = this;
        let arg = arguments; //arguments中存着e

        if (timeout) clearTimeout(timeout);

        timeout = setTimeout(()=> {
            fun.apply(_this,arg);
        },delay)
    }
}

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

我们依旧使用上述绑定 mousemove 事件的例子,通过上面的防抖函数,我们可以这么使用

content.onmousemove = debounce(count,1000);

可以看到,在触发事件后函数 1 秒后才执行,而如果我在触发事件后的 1 秒内又触发了事件,则会重新计算函数执行时间。

  • 函数防抖--立即执行
//立即执行
function debounce(fun, delay) {
    let timeout;
    return function () {
        let _this = this;
        let arg = arguments;

        if (timeout) clearTimeout(timeout);

        let applyNow = !timeout;

        timeout = setTimeout(()=> {
            timeout = null;
        },delay)

        if(applyNow) fun.apply(_this,arg);
    }
}

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

开发过程中的需求是多样的,为了适应各种需求,我们可以把上面两种情况都封装到一个方法里,根据实际情况选择调用。

function debounce(fun, delay, immediate) {
    //immediate为true时立即执行,反之为非立即执行
    let timeout;
    return function () {
        let _this = this;
        let arg = arguments;

        if (timeout) clearTimeout(timeout);

        if(immediate){
            //立即执行
            let applyNow = !timeout;

            timeout = setTimeout(()=> {
                timeout = null;
            },delay)

            if(applyNow) fun.apply(_this,arg);
        }else{
            //非立即执行
            timeout = setTimeout(()=>{
                fun.apply(_this,arg);
            },delay)
        }
    }
}

函数节流(throttle)

函数节流是指在连续触发事件时函数在 N 秒内只执行一次,节流会稀释函数的执行频率

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

  • 函数防抖--时间戳版
//时间戳版
function throttle(fun, delay) {
    let previous = 0;
    return function () {
        let _this = this;
        let arg = arguments;

        let now = Date.now();
        console.log(now);
        if(now - previous > delay){
            fun.apply(_this,arg);
            previous = now;
        }

    }
}
  • 函数防抖--定时器版
//定时器版
function throttle(fun, delay) {
    let timeout;
    return function () {
        let _this = this;
        let arg = arguments;

        if(!timeout){
            timeout = setTimeout(()=>{
                timeout = null;
                fun.apply(_this,arguments);
            },delay)
        }
    }
}