[转]面试题:什么是防抖节流?说一下它们的区别

[转]面试题:什么是防抖节流?说一下它们的区别

中间的示例图把防抖节流讲很清楚,理解目的后看代码就很简单,引用自:juejin.cn/post/6927276556599427079,作者:邑大学子_

一、前言

浏览器的 resizescrollkeypressmousemove 等事件在触发时,会极速地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能

为了优化体验,需要对这类事件进行调用次数的限制,对此我们可以采用debounce(防抖)和throttle(节流)的方式来减少调用频率,同时又不影响实际效果

下面来看一张图:

从上图中,可以看到:

  • 防抖是一段时间后只执行一次
  • 节流是在固定的频率执行

二、防抖(debounce)

防抖是当连续触发事件的时候,事件不会持续触发,当一定时间内没有触发事件后,事件函数才会执行

例如:就好像在百度搜索时,每次输入之后都有联想词弹出,这个控制联想词的方法就不可能是输入框内容一改变就触发的,它一定是当你结束输入一段时间之后才会触发

下面来举个例子

// 普通方案
let container = document.getElementById('container');

function getUserAction() {
    container.innerHTML = count++;
};
container.onmousemove = getUserAction;  // 鼠标移动会一直触发getUserAction函数

// 优化后的方案
function debounce(func, wait) {
    let timeout;

    return function () {
        let context = this; // 保存this指向
        let args = arguments; // 拿到event对象

        clearTimeout(timeout)
        timeout = setTimeout(function(){
            func.apply(context, args)
        }, wait);
    }
}
container.onmousemove = debounce(getUserAction, 1000); // 1s内不再触发后才会执行事件

这里作一下分析:

  • mousemove事件上绑定处理函数,这时 debounce 函数会立即调用,实际上绑定的函数是 debounce 函数内部返回的函数
  • 每一次事件被触发,都会清除当前的 timeout 然后重新设置超时调用
  • 只有在最后一次触发事件,才能在 wait 时间后执行

我们也可以为 debounce 函数加一个参数,可以选择是否立即执行函数

function debounce(func, wait, immediate) {

    let timeout;

    return function () {
        let context = this;
        let args = arguments;

        if (timeout) clearTimeout(timeout); // timeout 不为null
        if (immediate) {
            let callNow = !timeout; // 第一次会立即执行,以后只有事件执行后才会再次触发
            timeout = setTimeout(function(){
                timeout = null;
            }, wait)
            if (callNow) func.apply(context, args)
        }
        else {
            timeout = setTimeout(function(){
                func.apply(context, args)
            }, wait);
        }
    }
}

三、节流(throttle)

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

例如:浏览器播放事件,每隔一秒计算一次进度信息。

节流的实现方式可以分为两种:

第一种通过时间戳来实现,用一个变量保存上一次执行的时间,每次触发事件时生成一个时间戳,与上一次执行时间比较,如果大于给定的时间,则执行函数

function throttle(func, wait) {
    let context, args;
    let previous = 0;

    return function() {
        let now = +new Date();
        context = this;
        args = arguments;
        if (now - previous > wait) {
            func.apply(context, args);
            previous = now;
        }
    }
}

第二种通过定时器来实现,设置一个变量绑定一个定时器,通过if判断定时器是否存在,如果存在就不执行,当定时器执行时把变量置为null,这样在下一次触发事件时就可以设置定时器了

function throttle(func, wait) {
    var timeout;
    var previous = 0;

    return function() {
        context = this;
        args = arguments;
        if (!timeout) {
            timeout = setTimeout(function(){
                timeout = null;
                func.apply(context, args)
            }, wait)
        }

    }
}

比较这两种方法:

  • 第一种事件会立刻执行,第二种事件会在 n 秒后第一次执行
  • 第一种事件停止触发后没有办法再执行事件,第二种事件停止触发后依然会再执行一次事件

四、区别

防抖和节流的作用都是防止函数多次调用

区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于wait(参数wait),防抖的情况下只会调用一次,而节流的情况会每隔一定时间调用函数

五、结束语

如果觉得这篇文章对你有帮助,可以伸出你的小手,为这篇文章点个赞

我是前端路上一位新晋的萌新,怀着学习的态度,怀着认识各位同伴的心态,把自己的知识分享出来,除了让自己对知识认知更加巩固,也希望大家能够通过我写的文章学到一点微薄的知识,如果知识内容有误,可以在评论区或者下面公众号告诉我,我会立刻更改

最后,我也创建了一个 【前端收割机】的公众号,希望大家可以关注一波,里面的文章都是掉头发之作

本帖已被设为精华帖!
本帖由系统于 3年前 自动加精
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!