码字,杂谈

手动实现JS防抖

什么是防抖

事件响应函数在一段时间后才执行,如果在这段时间内再次调用,则重新计算执行时间;当预定的时间内没有再次调用该函数,则执行该函数。

防抖做什么

防止某些函数的频繁调用,保证页面的稳定流畅和数据准确性。

一个小的例子

使用 underscore 的防抖功能来测试一下效果。

中文网址

在页面中直接导入cdn即可。

https://cdn.bootcss.com/underscore.js/1.9.1/underscore.js

未防抖时的样子

将下面内容粘贴到一个HTML的body标签中。

<div id="container" style="width:100%;height:200px;line-height:200px;text-align:center;color:#fff;background-color:#444;font-size:30px;"></div>
<script>
    let count = 0;
    let container = document.querySelector("#container");

    // 此处为高频调用函数
    function doSomething() {
        container.innerHTML = count++;
    }

    container.onmousemove = doSomething;
</script>

这段代码会生成一个灰色框,只要鼠标在其内部移动,就会调用 doSomething 函数,导致数字不断增长。

使用防抖

同样地,将下面内容粘贴到一个新的HTML的body标签中。

<div id="container" style="width:100%;height:200px;line-height:200px;text-align:center;color:#fff;background-color:#444;font-size:30px;"></div>
<script src="https://cdn.bootcss.com/underscore.js/1.9.1/underscore.js"></script>
<script>
    let count = 0;
    let container = document.querySelector("#container");

    // 此处为高频调用函数
    function doSomething() {
        container.innerHTML = count++;
    }

    container.onmousemove = _.debounce(doSomething, 300);
</script>

这里导入了 underscore 库,这个库会导出一个 _ 的对象,它包含一个 debounce 方法,该方法的作用就是防抖。第一个参数是函数原型,第二个参数是响应时间,另外还可以设置是否立即执行,如果是,传入 true。这里我们设置300ms后响应。

这次运行后,可以发现当鼠标移动时,不再一味地增加,而是当鼠标静止或移出后一段时间(300ms)才会响应。

手动实现防抖函数

如果仅仅使用防抖函数,导入一个库是得不偿失的,因为防抖函数本身并不大,所以可以手写一个。

function debounce(func, wait, immediate) {
    let timeout, result;

    var debounced = function() {
        // 获取上下文,关联this的指向
        let context = this;
        // 获取所有参数
        const args = arguments;
        // 每次防抖都清除定时器,然后设置一个新的定时器。
        if (timeout) clearTimeout(timeout);

        // 区别是否立即执行
        if (immediate) {
            // 如果立即执行,需要一个变量控制重复执行。这里利用timeout取反,可以控制效果
            let callNow = !timeout;
            timeout = setTimeout(() => {
                timeout = null;
            }, wait);

            // 立即执行
            if (callNow) result = func.apply(context, args);
        } else {
            // 不立即执行,则正常定时器等待即可
            timeout = setTimeout(function() {
                result = func.apply(context, args);
            }, wait);
        }

        // 返回调用函数的结果
        return result;
    }

    // 设置一个清除函数,可以手动控制取消防抖函数的执行。
    debounced.cancel = function() {
        clearTimeout(timeout);
        timeout = null;
    }

    return debounced;
}

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注