从闭包理解节流

70 阅读2分钟

场景:限制事件触发频率,每隔多久执行一次,比如列表滚动事件监听是否滑动到底部 概念:触发事件会以固定频率触发,无论期间触发多少次 实现:事件戳和定时器;

<!DOCTYPE html>
<html lang="zh-cmn-Hans">

<head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge, chrome=1">
    <title>debounce</title>
    <style>
        #container{
            width: 100%; height: 200px; line-height: 200px; text-align: center; color: #fff; background-color: #444; font-size: 30px;
        }
    </style>
</head>

<body>
    <div id="container"></div>
    <script>
        var count = 1;
        var container = document.getElementById('container');
        function getUserAction() {
            container.innerHTML = count++;
        };
        container.onmousemove = getUserAction;
    </script>
</body>

</html>

以上代码表明div盒子中鼠标移动多少次就执行多少次数字累加,频率非常快。 场景还是和防抖一样的案例场景来介绍。

时间戳实现

获取每次事件触发的时间戳,和上次触发事件的时间戳作对比,如果这个差值大于我们设定的时间间隔,才执行事件,同时将初始化的时间戳更新为最新的时间戳[这里同样是需要一个变量previous来存储之前的时间戳,以和下一次执行的时间戳作比较判断间隔是否超过,超过则执行]。

function throttle(func, wait) {
    let previous = 0;
    retutrn function(){
        let now = new Date(); // 获取当前时间戳,值为一个当前时间到1970 年 1 月 1 日(UTC)起经过的毫秒数。
        if(now - previous > wait) { // 第一次就会执行
            func.apply(this, arguments);
            previous = now; // 将本次执行事件戳和下次执行事件时间戳作比较(即用来存储上一次的时间戳,同样的是利用了闭包)
        }
    }
}

调用

container.onmousemove = throttle(getUserAction, 1000)

定时器实现

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

触发事件后在延迟wait毫秒后执行,在事件执行的时候,timeout在定时器的回调中被重置为了null中,下一次事件触发又会进入!timeout判断中给timeout赋新的定时器(即让事件在下次执行)

两种实现方式方式的区别:时间戳是会立即执行,且最后一次不执行,定时器版本是会非立即执行,且最后一次会执行