防抖和节流分析

我心飞翔 分类:javascript

防抖

概念

防抖就是特定时间内,防止重复操作执行多次事件处理函数

即在一段连续的时间内,多次的同一操作(例如点击)对应的事件处理函数只触发一次

应用场景

  1. 登录、发请求等按钮避免用户点击太快,以致于发送了多次请求,需要防抖 --- 立即执行
  2. 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖 --- 非立即执行
  3. 输入框内容校验时,等用户输入完成后再校验,需要用到防抖 --- 非立即执行

实现方式

从上面两个应用场景,根据执行函数是否在操作后立即执行

防抖细分为以下两个类型,立即执行(场景1)和非立即执行(场景2/3)

接下来我们从点击按钮执行+1这个简单案例来实现两种防抖效果

首先看下不加防抖的代码和效果

<button id="btn1">不加防抖 +1</button>

<button id="btn2">防抖,函数非立即执行 +1</button>

<button id="btn3">防抖,函数立即执行 +1</button>
<div id="app">
    0
</div>
 
let count = 0;
function add() {
    const app = document.querySelector('#app');
    count++;
    app.innerHTML = `<span>${count}</span>`;
}
document.querySelector('#btn1').addEventListener('click', add);
 

无防抖

非立即执行

  1. 将事件处理函数放在定时器setTimeout内作为异步事件执行
  2. 当防抖函数被再次调用,判断上一个定时器是否存在,如果存在则清除上一个定时器
  3. 清除定时器达到只执行一次事件处理函数的目的

按照以上思路代码如下:

function debounce(fn, times, ...args) {
    let timer = null;
    return function () {
        const content = this;
        timer && clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(content, args);
        }, times);
    }
}
const addDebounce = debounce(add, 500);
document.querySelector('#btn2').addEventListener('click', addDebounce);
 

非立即执行

立即执行

立即执行和非立即执行的区别在于事件处理函数要先执行,后续的重复操作不会再次触发事件处理函数,所以事件函数不能放在setTimeout

  1. 事件处理函数放在setTimeout
  2. 通过判断上一个定时器是否存在来执行事件处理函数,如果上一个定时器不存在则执行事件处理函数,存在则清除定时器
  3. 定时器setTimeout内部清除自身

按照以上思路代码如下:

function debounceImmediately(fn, times, ...args) {
    let timer = null;
    return function () {
        timer && clearTimeout(timer);
        let isDone = !timer;
        timer = setTimeout(() => {
            timer = null
        }, times);
        isDone && fn.apply(this,args)
    }
}
const addDebounceImmediately = debounceImmediately(add, 500)
document.querySelector('#btn3').addEventListener('click', addDebounceImmediately);
 

立即执行

节流

概念

节流就是减少事件处理的频次

即在一高频触发的场景下(例如scroll事件),截取一段连续的时间,该时间内事件只触发一次,达到降低事件处理函数执行的频次

应用场景

  1. 图片懒加载,滚动条下滑以一定的频率被触发图片加载
  2. mousemove事件,每隔一段时间变动坐标

实现方式

接下来我们就以mousemove事件,每次移动+1,作为一个简单案例来说明节流

先看下不加节流时的效果

let count = 0;

function add() {
    const app = document.querySelector('#app');
    count++;
    app.innerHTML = `<span>${count}</span>`;
}
document.addEventListener('mousemove',add);
 

无节流

状态锁

  1. 节流函数初始状态设置为true,当状态为true允许定时器执行
  2. 一旦开启定时器,状态设置为false,不允许下一次的节流函数执行
  3. 事件处理函数放在setTimeout内,当定时器时间到达,事件处理函数执行,状态设置为true

按照以上思路代码如下:

function throttle(fn, times, ...args) {
    let isDone = true;
    return function () {
        if (isDone) {
            const context = this;
            setTimeout(() => {
                fn.call(context, ...args);
                isDone = true;
            }, times);
            isDone = false;
        }
    }
}
const addThrottle = throttle(add,500);
document.addEventListener('mousemove',addThrottle);
 

节流

时间戳

  1. 定义初始时间 startTime 为0,节流函数执行获得当前时间 nowTime=Date.now()
  2. nowTime - startTime > times 执行事件处理函数,重置 startTime=Date.now() 为当前时间

按照以上思路代码如下:

function throttleByTime(fn, times, ...args) {
    let startTime = 0;
    return function () {
        const context = this;
        let nowTime = Date.now();
        if (nowTime - startTime > times) {
            fn.call(context, ...args);
            startTime = nowTime;
        }
    }
}
const addThrottleByTime = throttleByTime(add, 500);
document.addEventListener('mousemove', addThrottleByTime);
 

节流

总结

相同点:防抖和节流的核心都是在一定的时间内,只能执行一次事件处理函数

不同点:

  • 防抖是避免相同的操作执行多次

  • 节流是降低高频事件触发的频次

回复

我来回复
  • 暂无回复内容