品味函数防抖与函数节流

1、情景重现

当我们实现一个功能,希望实现输入框输入值变化,自动发起搜索请求,我们可能会写出如下代码:

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        input {
            padding: 10px;
            border: 1px solid #666;
            font-size: 18px;
        }
    </style>
</head>

<body>
    <input type="text" id="input" placeholder="输入账号">
    <script>
        var input = document.getElementById('input');
        input.oninput = function () {
            console.log(input.value)
        }
    </script>
</body>

</html>

非常的简单,利用oninput侦测input中value的变化,打印当前的值。但是问题来了,每次变化都会发起请求,于是发生如下情景:
在这里插入图片描述
这是一件非常头疼的事情,仅仅是想搜索测试两个字,却(假装)发起了7次请求。试想当1000个用户同时做一件这样的事,服务器将会接受7000次请求,显然不够经济。
在这里插入图片描述
同样的情况,window.onresize、window.mousemove触发频率相当高,解决这种困境需要什么方法呢?

2、防抖和节流

函数防抖和函数节流就是非常经典的解决方法。那么这两者有什么区别呢?

  • 函数防抖:指触发事件后在规定时间内回调函数只能执行一次,如果在规定时间内又触发了该事件,则会重新开始计时。
  • 函数节流:当持续触发事件时,在规定时间段内只能调用一次回调函数。如果在规定时间内又触发了该事件,则什么也不做,也不会像防抖函数一样重新开始计时。

3、函数防抖

应用场景
两个条件:
1、连续的操作会导致频繁的事件回调
2、用户只关心"最后一次"操作的反馈

原理:两者原理是一致的,都是借助计时器setTimeout来实现回调频率的限制。通过定时器将回调函数进行延时,如果在规定时间内继续回调,发现存在之前的定时器,则将该定时器清除,并重新设置定时器。
  1. 非立即执行版:事件触发->延时->执行回调函数;如果在延时中,继续触发事件,则会重新进行延时,在延时结束后执行回调函数。常见例子:就是input搜索框,客户输完过一会就会自动搜索。
  2. 立即执行版:事件触发->执行回调函数->延时;如果在延时中,继续触发事件,则会重新进行延时。在延时结束后,并不会执行回调函数。常见例子:就是对于按钮防点击,例如点赞、心标、收藏等有立即反馈的按钮。
    我们以上文提到的情景,实现一个函数防抖:
var input = document.getElementById('input');

input.oninput = debounce(function(){
    console.log(input.value)
}, 1000)

// 非立即执行
function debounce(fn, delay) {
    var timer = null;
    return function () {
        clearTimeout(timer)
        timer = setTimeout(function () {
            fn.apply(this)
        }, delay)
    }
}

// 立即执行
function debounce(fn, delay) {
    var timer = null;
    return function () {
        if(!timer){
        	fn.apply(this)
        }
        setTimeout(function () {
            clearTimeout(timer)
        }, delay)
    }
}

我们在侦测到value值变化的时候会创建一个计时器,待计时结束之后执行回调函数,但如果在计时间隙,又再一次触发,则会销毁之前的计时器,重新开始计时。关键之处,就是后面所有的回调函数都能访问到之前设置的定时器time,在这里使用闭包,效果如下:
在这里插入图片描述

4、函数节流

应用场景:
两个条件:
1、客户连续频繁地触发事件(事件触发的时间间隔至少是要比规定的时间要短)
2、在不断操作过程中的反馈。
例如:鼠标移动事件,滚动事件等

var input = document.getElementById('input');

input.oninput = throttle(function () {
    console.log(input.value)
}, 1000)

function throttle(fn, delay) {
	// 记录上一次触发的时间戳,初始设为0,确保第一次触发会执行回调
    var lastTime = 0;
    return function () {
        var nowTime = Date.now();
        // 如果时间差大于规定时间,则执行回调
        if (nowTime - lastTime > delay) {
            fn.apply(this)
            lastTime = nowTime;
        }
    }
}

我们在侦测到value值变化的时候会记录下当前时间,只有当,效果如下:
在这里插入图片描述
还有另外一种实现方式,和防抖有异曲同工之妙,借助计时器。但区别在于,设置的计时器不会被中途销毁,必须等到计时结束才能进行下一个回调。

//定时器版:
function throttle(fun, delay = 1000) {
    let timer;
    return function(args) {
        let that = this;
        //如果定时器不存在,则设置定时器,到时后,才执行回调,并将定时器设为null
        if (!timer) { 
            timer = setTimeout(function(){
                timer = null;
                fun.apply(that)
            }, delay)
        }
    }
}

感谢大佬的文章,有所启发:https://juejin.im/post/5c6bab91f265da2dd94c9f9e

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值