likes
comments
collection
share

教你如何手搓一个”防抖“函数

作者站长头像
站长
· 阅读数 58

前言

防抖(debounce) 函数是一种常用的前端编程技术,用于减少函数的执行频率。当一个事件被频繁触发时,防抖函数可以确保只有在事件停止触发一定时间后才执行对应的处理函数。

通常的实现方式是在事件被触发时设置一个定时器,在指定的时间间隔内如果事件没有再次触发,则执行对应的处理函数,如果事件再次触发,则重新设置定时器并且销毁之前的定时器。

开始写

要给提交按钮绑定一个点击事件:写一个script标签,用 document.getElementById()用来获取 HTML 文档中指定 ID 的元素,即获取到button按钮,再使用addEventListener()方法监听一个click事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<button id="btn">提交</button>

<script>
    let btn = document.getElementById('btn') // 以id来获取一个html结构,帮助拿到button按钮

    btn.addEventListener('click', function() {
        console.log('提交')
    }) // 给button按钮监听一个click事件
</script>
</body>
</html>

在控制台点击提交按钮会打印提交两个字,一直点会一直打印提交

教你如何手搓一个”防抖“函数

于是我们设置一个定时器setTimeout(),当点击按钮时就会触发回调函数,在控制台打印提交。但是并没有真正解决问题,多点几次就是多创建了几个定时器而已,每个定时器过1s后都会继续执行。

<script>
    let btn = document.getElementById('btn') // 以id来获取一个html结构,帮助拿到button按钮

    btn.addEventListener('click', function() {
        setTimeout(function() {
            console.log('提交')
        }, 1000)
    }) // 给button按钮监听一个click事件
</script>

我们就需要设计一个防抖函数来销毁多次点击所产生的定时器

     // 防抖函数
        function debounce(fn) {
            let timer = null
            return function() {
                clearTimeout(timer)
                timer = setTimeout(fn, 1000)
            }
            
        }

addEventListener()就会触发回调函数debounce, 返回一个function函数体,并且debounce执行完毕后会产生一个闭包,里面包含子函数function执行要引用的变量timer,当子函数执行时,会现在自己的执行上下文的词法环境找,再去自己的变量环境找,再去debounce留下的闭包找,直到找到timer。

使用闭包的目的:保留timer的值不被销毁,使得每次function被调用时都能找到timer。

教你如何手搓一个”防抖“函数

每次触发点击事件时function函数都会被执行,clearTimeout(timer)都会清除timer定时器,并且setTimerout()会创建一个新的定时器赋给timer,若点击的时间间隔小于1s, handle还没来得及执行完定时器就被销毁了,直到每次点击间隔时间恢复正常。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button id="btn">提交</button>
    <script>
        let btn = document.getElementById("btn");

        function handle() {
            console.log('提交');
            
        }
        btn.addEventListener("click", debounce(handle));

        // 防抖函数
        function debounce(fn) {
            let timer = null
            return function() {
                clearTimeout(timer)
                timer = setTimeout(fn, 1000)
            }
            
        }

    </script>
</body>
</html>

this的指向

<body>
    <button id="btn">提交</button>
    <script>
        let btn = document.getElementById("btn");

        function handle() {
            console.log('提交', this);
            
        }
        btn.addEventListener("click", handle);

        // 防抖函数
        function debounce(fn) {
            let timer = null
            return function() {
                clearTimeout(timer)
                timer = setTimeout(fn, 1000)
            }
            
        }

    </script>
</body>

教你如何手搓一个”防抖“函数

此时handle中的this指向btn, 而不是window,因为handle不是独立调用,而是在addEventListener()方法中被直接调用。

<body>
    <button id="btn">提交</button>
    <script>
        let btn = document.getElementById("btn");

        function handle() {
            console.log('提交', this);
            
        }
        btn.addEventListener("click", debounce(handle));

        // 防抖函数
        function debounce(fn) {
            let timer = null
            return function() {
                clearTimeout(timer)
                timer = setTimeout(fn, 1000)
            }
            
        }

    </script>
</body>

教你如何手搓一个”防抖“函数

btn.addEventListener("click", debounce(handle));这样写会让handle中的this指向window,因为handle被setTimeout()调用,定时器setTimeout里面的回调函数的this默认指向window。

那该如何把this的指向改回btn上呢?

btn.addEventListener("click", debounce(handle));因为addEventListener执行之后debounce会返回一个子函数function,所以function中this的指向会是btn。于是timer = setTimeout(fn, 1000)把这行代码纠正一下:

    timer = setTimeout(() => {
        fn.call(this)
    }, 1000)

还可以通过设置一个that来实现同样的效果

            return function() {
            const that = this
                clearTimeout(timer)
                timer = setTimeout(function() {
                    fn.call(that)
                }, 1000)
            }

这样写是把外面function里面的this拿到里面的function去使用,但this还是外层function的

事件参数 e

事件参数通常是指在事件发生时传递给事件处理程序的数据。这些参数可以包含有关事件本身的信息,如事件类型、触发事件的对象、鼠标位置等。具体的内容取决于编程语言、框架或库的实现。例如,在Web开发中,事件参数可能是包含HTTP请求信息的对象,而在GUI编程中,事件参数可能包含有关鼠标点击或键盘按键的信息。

原来的函数的默认参数e为:

<script>
    let btn = document.getElementById('btn');

    function handle(e) {
        // ajax请求
        console.log('提交', this, e);
    }
    btn.addEventListener('click', handle);// this 指向btn
    
    // 防抖函数
    function debounce(fn) {
        let timer = null;
      
        return function(e) {
            // 如果第二次的时间没到1s,就销毁上一次的定时器
        clearTimeout(timer);
        timer = setTimeout(()=>{ 
            fn.call(this, e);
           }, 1000);
        }
    }
</script>

教你如何手搓一个”防抖“函数

设置防抖函数后的e为undefined:

<script>
    let btn = document.getElementById('btn');

    function handle(e) {
        // ajax请求
        console.log('提交', e);
    }
    btn.addEventListener('click', debounce(handle));// this 指向window
    
    // 防抖函数
    function debounce(fn) {
        let timer = null;
      
        return function() {
            // 如果第二次的时间没到1s,就销毁上一次的定时器
        clearTimeout(timer);
        timer = setTimeout(()=>{ 
            fn.call(this);
           }, 1000);
        }
    }
</script>

教你如何手搓一个”防抖“函数

如何解决这个默认参数的问题

通过call将默认参数e传进去

<script>
    let btn = document.getElementById('btn');

    function handle(e) {
        // ajax请求
        console.log('提交', e);
    }
    btn.addEventListener('click', debounce(handle));// this 指向window
    
    // 防抖函数
    function debounce(fn) {
        let timer = null;
      
        return function(e) {
            // 如果第二次的时间没到1s,就销毁上一次的定时器
        clearTimeout(timer);
        timer = setTimeout(()=>{ 
            fn.call(this, e);
           }, 1000);
        }
    }
</script>

教你如何手搓一个”防抖“函数

结尾

学会了的话自己也来尝试手搓一个防抖函数吧

教你如何手搓一个”防抖“函数

转载自:https://juejin.cn/post/7367659849100771343
评论
请登录