【Javascript专题一】函数防抖Debounce工具封装及应用场景总结

本文深入解析函数防抖的实现原理,包括四种版本的防抖函数,立即执行版、带返回值版以及具备取消功能的防抖函数。同时,探讨了防抖在鼠标移动、页面缩放、滚动条等事件中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

函数防抖及应用场景总结

环境初始化

<!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>
<button id="button">cancel</button>
<script src="debounce.js"></script>
</body>

</html>

函数防抖第一版

// 环境初始化
let count = 1;
let container = document.getElementById('container');

function handleUserAction(e){
    console.log(this, e);
    container.innerHTML = count++;

    // 如果这个函数有返回值的话
    return {
        status: true,
        msg: 'execute success'
    }
}

// 滑动的时候会不断地触发页面的move函数,性能损耗巨大
container.onmousemove = handleUserAction;


/*
* 防抖原理:你尽管触发事件,但是我一定在事件触发 n 秒后才执行,
* 如果你在一个事件触发的 n 秒内又触发了这个事件,
* 那我就以新的事件的时间为准,n 秒后才执行!!!
* */
// TODO: 防抖就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
// v1. 防抖第一版
function debounce(fn, wait) {
    let timeout;
    return function () {
        clearTimeout(timeout);
        timeout = setTimeout(fn, wait);
    }
}

container.onmousemove = debounce(handleUserAction, 1000);
// TODO: 如果不使用debounce函数的时候,this就是container DOM元素;当使用之后,this指向window?


如果不使用debounce函数的时候,this就是container DOM元素;当使用之后,this指向window?(已解决)

// v2. 防抖第二版
function debounce(fn, wait) {
    let timeout;
    return function () {
        let context = this;

        clearTimeout(timeout);

        timeout = setTimeout(function () {
            fn.apply(context);
        }, wait);
    }
}

如果不使用debounce函数的时候,handleUserAction的参数就是Event; 使用之后变成undefined?(已解决)

// v3: 防抖第三版
function debounce(fn, wait) {
    let timeout;

    return function () {
        let context = this;
        let args = arguments;

        clearTimeout(timeout);

        timeout = setTimeout(function () {
            // 把当前的环境下的this和变量参数传过去
            console.log(args)
            // apply 也是可以直接传递一个伪数组arguments的,也可以是Array数组对象
            fn.apply(context, args);
        }, wait);
    }
}

我不希望非要等到事件停止触发后才执行,我希望立刻执行函数,然后等到停止触发 n 秒后,才可以重新触发执行呢?(已解决)

// TODO: 立即执行版的意思是触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果。
// v4: 防抖第四版
function debounce(fn, wait, immediate) {
    let timeout;

    return function () {
        let context = this;
        let args = arguments;

        if (timeout) {
            clearTimeout(timeout);
        }

        // 根据传入的参数判断是否立即执行当前的事件
        if (immediate) {
            // 如果已经执行过的话,就不再执行, 需要在执行之前,提前存一份,防止下一句代码重新生成了一个定时器
            let callNow = !timeout;         // !表示取反, !!表示转换为bool值
            // 开始重新生成一个全新的定时器
            timeout = setTimeout(function () {
                timeout = null;
            }, wait);

            // 如果之前就没有timeout定时器的话,就去执行
            if (callNow) {
                console.log('run now')
                // 立即执行
                fn.apply(context, args);
            }
        }
        else {
            // 默认的情况处理
            timeout = setTimeout(function () {
                let res = fn.apply(context, args);
                console.log('get result:', res);
            }, wait);
        }
    }
}
container.onmousemove = debounce(handleUserAction, 1000, true);

如何给我的防抖函数加上返回值呢?(已解决)

// v5: 防抖第5版
/**
 * 设计一个函数防抖的函数
 * @param fn
 * @param wait
 * @param immediate
 * @return {Function}
 */
function debounce(fn, wait, immediate) {
    let timeout;            // 定时器对象
    let result;             // handleUserAction函数的执行结果

    return function () {
        let context =  this;
        let args = arguments;

        if (timeout) {
            clearTimeout(timeout);
        }

        if (immediate) {
            // 立即执行的话
            let callNow = !timeout;
            timeout = setTimeout(function () {
                timeout = null;
            }, wait);

            if (callNow) {
                result =  fn.apply(context, args);
            }
        }
        else {
            // 传统的方式的执行的话
            timeout = setTimeout(function () {
                // 由于这里实际上就是一个异步的执行结果,所有此时的result最后每次都会返回的是一个undefined
                result =  fn.apply(context, args);
            }, wait);
        }

        // 在闭包函数的最后,返回函数执行的结果
        console.log('execute result: ', result);
        return result;
    }

}

container.onmousemove = debounce(handleUserAction, 1000, false);

如何实现防抖的取消功能?(已解决)

// TODO: 实现防抖功能的取消?
// v6:防抖第六版,新增取消防抖的功能; 只有immediate的时候才处理返回的结果
function debounce(fn, wait, immediate) {
    let timeout;
    let result;

    // 创建一个debounce防抖函数
    let debounced =  function () {
        let context = this;
        let args = arguments;

        if (timeout) {
            clearTimeout(timeout);
        }

        if (immediate) {
            let callNow = !timeout;
            timeout = setTimeout(function () {
                timeout = null;
            }, wait);

            if (callNow) {
                result = fn.apply(context, args);
            }
        }
        else {
            timeout = setTimeout(function () {
                fn.apply(context, args);
            }, wait);
        }

        return result;
    };

    // 防抖函数新增cancel函数
    debounced.cancel = function () {
        console.log('cancel finished!', timeout);
        clearTimeout(timeout);
        timeout = null;
    };

    return debounced;
}


// 1. 创建一个debounce函数
let setUserAction = debounce(handleUserAction, 10000, true);;

// 2. 设置当前的回调函数
container.onmousemove = setUserAction;

// 3. 监听按钮的取消事件(并不是取消了整个debounce,点击按钮之后,可以直接触发一次事件;每次点击就可以立即触发当前的事件,而不需要等待10s)
document.getElementById("button").addEventListener('click', function(){
    console.log(setUserAction)
    setUserAction.cancel();
})

Debounce防抖应用场景:

  1. 鼠标/触摸屏的mouseover/touchmove事件
  2. 页面窗口的resize事件
  3. 滚动条的scroll事件

参考博文:https://github.com/mqyqingfeng/Blog/issues/22

### Element Plus 的 `el-table` 组件隐藏表头方法 在使用 Element Plus 的 `el-table` 组件时,如果希望隐藏表头,可以通过设置 CSS 样式来实现这一需求。具体来说,在定义表格样式时可以利用 `.hidden-header` 类名,并通过覆盖默认样式的方式使表头不可见。 对于 Vue 3 和 Element Plus 结合使用的场景下,可以在模板部分为 `<el-table>` 添加自定义类名以便于样式控制: ```html <template> <div> <!-- 使用 hiddenHeaderClass 来应用特定样式 --> <el-table :data="tableData" class="hidden-header"> <el-table-column prop="date" label="日期"></el-table-column> <el-table-column prop="name" label="姓名"></el-table-column> <el-table-column prop="address" label="地址"></el-table-column> </el-table> </div> </template> <script setup> import { ref } from 'vue'; const tableData = ref([ { date: '2016-05-03', name: 'Tom', address: 'No. 189, Grove St, Los Angeles' }, // ...其他数据项... ]); </script> <style scoped> /* 定义用于隐藏表头的CSS */ .hidden-header ::v-deep th.el-table__cell.is-leaf, .hidden-header ::v-deep .el-table__header-wrapper tr:first-child th { display: none; } </style> ``` 上述代码片段展示了如何创建一个带有隐藏表头样式的 `el-table` 实例[^1]。需要注意的是,这里采用了 `::v-deep` 深度选择器来穿透作用域以影响内部组件样式;另外,`.is-leaf` 及其子元素的选择是为了确保只针对实际显示的数据列进行操作而不干扰分组或其他特殊类型的头部单元格。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值