Java教程

[JavaScript] 手写实现一个防抖函数

本文主要是介绍[JavaScript] 手写实现一个防抖函数,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1. 什么是防抖

​ 顾名思义,防抖就是防止抖动,我们在发抖的时候是不是不停地动呢,防抖就是只动一次就好了,不要一直抖。

​ 防抖就是在一段时间内,将连续的多次触发事件,转化为只触发一次,可以是只触发第一次,也可以是只触发最后一次

​ 在实际应用中,对于那些触发很快的事件,如在input中输入搜索的内容,从服务端返回数据,我们肯定不希望在我们还没有输入完成的时候请求就发出去,这是很低性能的行为,因此我们需要手动进行防抖

​ 我们来实现一下吧

2. 防抖的实现(简易版本)

先建立一个简单的使用场景,省略一些html标签,相信大家很容易看懂

<style>
        #container{
            width: 100%; 
            height: 200px; 
            line-height: 200px; 
            text-align: center; 
            color: #fff; 
            background-color: #444; 
            font-size: 30px;
        }
</style>

<body>
	<div id="container"></div>
</body>
// 定义count,我们的事件就是要让count不停累加
var count = 1;
var container = document.getElementById('container');
// 累加方法
function getUserAction() {
        container.innerHTML = count++;
    };

第一版:

实现原理:利用闭包与定时器,闭包的作用就是将timeout给记住,(不太了解闭包可以看闭包 - JavaScript | MDN)每次触发鼠标移动事件,先清除定时器,再重新设置一个新的定时器,定时器时间到了才会触发事件

const debounce1 = (func, delay) => {
    let timeout;
    return function() {
        clearTimeout(timeout);
        timeout = setTimeout(func, delay);
    }
}

// 测试
// 可以看到,鼠标移动一下,1s后数字加1,鼠标移动后1s内继续移动,数字不会加1,只有当鼠标停止移动1s后,数字才会加1
container.onmousemove = (debounce1(getUserAction, 1000));

当然,简易版存在着一些问题

问题一:this的指向发生了改变

我们对绑定的元素触发事件的回调函数时,事件函数的this应该是指向被绑定元素的,也就是<div id="container"></div>,但是由于我们将事件的回调函数作为参数传给了debounce函数,因此这里的this指向发生了隐式丢失,指向了window

问题二:事件对象发生了改变

我们在调用事件时,会给事件回调函数传入事件对象event,但是由于我们对事件回调函数使用debounce进行了封装,返回出来的函数中的事件对象已经丢失了

3. 防抖的实现(改进版)

改进的方法:将this原来的指向和event对象传入闭包中

const debounce2 = (func, delay) => {
    let timeout;
    return function() {
        // 保存this指向
        let context = this;
        // 保存调用事件回调函数时传入的参数(event对象)
        let args = arguments;
        clearTimeout(timeout);
        timeout = setTimeout(function() {
            func.apply(context, args)
        }, delay)
    }
}
// 测试
function getUserAction(e) {
    	// 测试event对象
        console.log(e);
    	// 测试this指向
        console.log(this);
        container.innerHTML = count++;
    };
container.onmousemove = (debounce2(getUserAction, 1000));

4. 防抖的实现(升级版)

前面有说到,防抖就是在一段时间内,将连续的多次触发事件,转化为只触发一次,可以是只触发第一次,也可以是只触发最后一次

我们前面实现的是触发的最后一次才调用事件的回调函数,那么我们可不可以控制,让事件在第一次触发时就被调用,在规定时间内不再被调用呢?当然可以!

使用immediate参数作为控制事件回调函数何时调用的判断条件,true则立即调用,false则在最后一次触发才调用

const debounce3 = (func, delay, immediate) => {
    let timeout, result;
    return function() {
        // 保存this指向
        let context = this;
        // 保存调用事件回调函数时传入的参数(event对象)
        let args = arguments;
        if (timeout) clearTimeout(timeout);
        if (immediate) {
            let callNow = !timeout;
            timeout = setTimeout(function() {
                timeout = null;
            }, delay)
            if (callNow) {
                return result = func.apply(context, args);
            }
        } else {
            timeout = setTimeout(function() {
            func.apply(context, args)
        }, delay);
        }
    }
    return result;
}

// 测试
container.onmousemove = debounce3(getUserAction, 2000, true)

5. 总结

本文探讨了防抖的实现与应用,对于防抖的实现,要注意闭包调用时参数传递的丢失和this指向的丢失,同时还可以实现一个可以控制在什么时候触发的防抖函数。

本篇文章就到这里啦,下次我们聊一聊和防抖很像的节流

这篇关于[JavaScript] 手写实现一个防抖函数的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!