继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

一起扫荡JavaScript(十)—— 防抖与节流

dorseyCh
关注TA
已关注
手记 50
粉丝 1.3万
获赞 1519

  防抖和节流其实是一种思想,是一种性能优化的方案,并非JavaScript独有,只不过JavaScript多运行于用户端(也就是前端),而用户端的交互是很频繁的,有不少的请求或交互实际上是重复的。而这就给了防抖和节流很大的用武之地。


  ① 防抖

  防抖,顾名思义,防止手抖,曾经多少英雄豪杰,因为一时手抖擦枪走火而悔恨终身,也曾经多少富甲一方的商贾因为手抖多打了个0而家破人亡。

  防抖的作用就是无论你是手滑了还是手抖了,还是打多了一个0,它仍然会沿着旧有的节奏走,从而防止手滑导致的一系列问题。

  防抖的实现思路大体上是这样:

  当持续触发事件时(比如用户输入时不断的键盘敲击),一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。

  可以看看下面的函数:


const debounce = (fn, wait) => {

    let timeout = null;

    return function () {

        timeout !== null && clearTimeout(timeout);

        timeout = setTimeout( fn, wait );
    }
};

  定时器一边在走,而这边继续监听触发,假如两次触发的时间间隔大于设置的防抖延迟,则这时候定时器里的函数已经执行了,而假如小于,定时器里的函数还没来得及执行,定时器就被清除了。

  看一下调用,假如此时有一块DOM结构是这样的话。

<p class='shake'></p>

在调用上方的防抖函数debounce。如下:

const debounceDiv = () => console.log('debounceDiv'); // 定义一下需要触发的函数

document.querySelector('.shake').addEventListener('mousemove', debounce(debounceDiv, 300));

  鼠标滑动的触发频率是很高的,可能不小心手抖鼠标滑动了一大下,这时候可能某个函数就被执行了好几十次,而处于中间的那些可能实际上我们并不需要,而通过防抖机制,尽管这个函数所处的环境被触发了几十次,但该函数实际上只会执行最后的那一次,这样就节省了资源,提高了性能。

  再来看防抖实际应用中很多的例子 —— 输入。

  假如你现在在编辑Markdown文本,或者搜索栏的一些输入,而这时候可能JavaScript做的一个监听动作就是你键盘的按下或抬起动作,但相比你打的字而言,你的键盘敲击速度实在是太快了,快到可能接近程序的一些操作或者搜索栏的一些结果联想,甚至比之还快,但就如这一个简单的打字而言,用户刚刚打了一个f,程序不管不顾立马执行,这没有问题,但可能用户是想打一个“防”字,他对f是几乎无察觉无认知的,而这部分假如还去执行结果,无疑是一种极大的浪费。

  看看代码,由于这时候我们需要在一个恰当的时机,将用户输入的值打印到界面上,原来的debounce 函数需要稍微变一下,将监听的this值做下绑定:
  变成:


const debounce = (fn, wait) => {

    let timeout = null;

    return function () {

        timeout !== null && clearTimeout(timeout);

        let _self = this;

        timeout = setTimeout( () => fn.call(_self), wait);
    }
};

  再看一下应用:

const debounceInput = function () { document.querySelector('.shake').innerHTML = this.value; }

document.querySelector('.shake-input').addEventListener('keyup', debounce.call(this, debounceInput, 300));

  这样10行代码左右也就简单的完成了我们的防抖。

  ② 节流

  节流,也顾名思义 —— 任尓滔滔江水,我自涓涓细流。它就像一个水闸,或者说管道,尽管可能某段时间内这个事件触发了很多次,但在这里,统统无效,只会每间隔一定时间触发一次。它更贴切的一个描述可以看下图:
图片描述

  当持续触发事件时,保证一定时间段内只调用一次事件处理函数,这就是节流。


const throttle = (fn, delay) => {

    let timeNode = Date.now();

    return function () {

        let time = Date.now();

        if(time - timeNode >= delay) {

            fn.call(this);

            timeNode = Date.now();
        }
    }
}

const throttleDiv = () => console.log('throttleDiv');

const throttleInput = function () { document.querySelector('.throttle').innerHTML = this.value; }

document.querySelector('.throttle').addEventListener('mousemove', throttle(throttleDiv, 300));

document.querySelector('.throttle-input').addEventListener('keyup', throttle.call(this, throttleInput, 300));

最后吧,贴一下完整的测试用例。

html部分:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .container{
            display: inline-block;
        }
        .shake, .throttle{
            width: 300px;
            height: 300px;
            border: 1px solid #ccc;
            word-break:break-all;
            padding: 10px;
            box-sizing: border-box;
        }
        .shake-input, .throttle-input{
            width: 300px;
            line-height: 35px;
            border: 1px solid #ccc;
            box-sizing: border-box;
        }
    </style>
</head>
<body>
    <div class='container'>
        <p class='shake'></p>
        <input type="text" class='shake-input'>
    </div>
    <div class='container'>
        <p class='throttle'></p>
        <input type="text" class='throttle-input'>
    </div>
    <script src='./test.js'></script>
</body>
</html>

js部分



//  防抖
// 函数防抖(debounce):当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时

const debounce = (fn, wait) => {

    let timeout = null;

    return function () {

        timeout !== null && clearTimeout(timeout);

        let _self = this;

        timeout = setTimeout( () => fn.call(_self), wait);
    }
};

const debounceDiv = () => console.log('debounceDiv');

const debounceInput = function () { document.querySelector('.shake').innerHTML = this.value; }


document.querySelector('.shake').addEventListener('mousemove', debounce(debounceDiv, 300));

document.querySelector('.shake-input').addEventListener('keyup', debounce.call(this, debounceInput, 300));


//  节流

const throttle = (fn, delay) => {

    let timeNode = Date.now();

    return function () {

        let time = Date.now();

        if(time - timeNode >= delay) {

            fn.call(this);

            timeNode = Date.now();
        }
    }
}

const throttleDiv = () => console.log('throttleDiv');

const throttleInput = function () { document.querySelector('.throttle').innerHTML = this.value; }

document.querySelector('.throttle').addEventListener('mousemove', throttle(throttleDiv, 300));

document.querySelector('.throttle-input').addEventListener('keyup', throttle.call(this, throttleInput, 300));

  好了,防抖和节流就先说到这了,以后有想到的再补充吧。

打开App,阅读手记
1人推荐
发表评论
随时随地看视频慕课网APP