前言:

防抖(Debounce) 和 节流(Throttle) 技术用于限制函数执行的次数。通常,一个函数将被执行多少次或何时执行由开发人员决定。但在某些情况下,开发人员会将这种能力赋予用户,由用户决定执行该功能的时间和次数。

例如,添加到clickscrollresize等事件上的函数,允许用户决定何时执行它们以及执行多少次。有时,用户可能会比所需更频繁地执行这些操作。这可能不利于网站的性能,特别是如果附加到这些事件的函数执行一些繁重的计算。在这种情况下,用户可以控制函数的执行,开发人员必须设计一些技术来限制用户可以执行函数的次数。

举个例子,假设我们为滚动事件scroll添加了一个函数,该函数中会执行修改DOM元素的操作。我们知道,修改DOM元素大小开销很大,会引起浏览器的回流(Reflow)和重排(Repaint),以及重新渲染整个或部分页面。如果用户频繁滚动,导致该函数频繁被调用,可能会影响网页性能或导致页面卡顿等。
此外,有些事件回调函数中包含ajax等异步操作的时候,多次触发会导致返回的内容结果顺序不一致,而导致得到的结果非最后一次触发事件对应的结果

所以,为了优化网页的性能,控制函数被调用的频率是很有必要的,防抖(Debounce) 和 节流(Throttle) 是通过控制函数被调用的频率来优化脚本性能的两种方法

一、防抖(Debounce)

防抖:无论用户触发多少次事件,对应的回调函数只会在事件停止触发指定事件后执行。(即:回调函数在事件停止触发指定时间后被调用)

例如,假设用户在 100 毫秒内点击了 5 次按钮。防抖技术不会让这些点击中的任何一个执行对应的回调函数。一旦用户停止点击,如果去抖时间为 100 毫秒,则回调函数将在 100 毫秒后执行。因此,肉眼看来,防抖就像将多个事件组合成一个事件一样。

1.1 防抖函数的实现

(1)版本1 —— 停止触发事件n毫秒后执行回调函数

触发事件后函数不会立即执行,而是在停止事件触发后 n 毫秒后执行,如果在 n 毫秒内又触发了事件,则会重新计时

/**
* @desc 函数防抖
* @param func 回调函数
* @param delay 延迟执行毫秒数
*/
function debounce(func, delay) {
    let timer;  // 定时器

    return function () { 
        let context = this;  // 记录 this 值,防止在回调函数中丢失
        let args = arguments;  // 函数参数

        //如果定时器存在,则清除定时器(如果没有,也没必要进行处理)
        timer ? clearTimeout(timer) : null; 

        timer = setTimeout(() => { 
            // 防止 this 值变为 window
            func.apply(context, args) 
        }, delay);
    }
} 

(2)版本2

触发事件后立即执行回调函数,但是触发后n毫秒内不会再执行回调函数,如果 n 毫秒内触发了事件,也会重新计时。

 /**
* @desc 函数防抖
* @param func 回调函数
* @param delay 延迟执行毫秒数
*/ 
function _debounce(func, delay) {
    let timer;  // 定时器

    return function () {
        let context = this;  // 记录 this 值,防止在回调函数中丢失
        let args = arguments;  // 函数参数

        // 标识是否立即执行
        let isImmediately = !timer;

        //如果定时器存在,则清除定时器(如果没有,也没必要进行处理)
        timer ? clearTimeout(timer) : null;

        timer = setTimeout(() => {
            timer = null;
        }, delay);

        // isImmediately 为 true 则 执行函数(即首次触发事件)
        isImmediately ? func.apply(context, args) : null;
    }
} 

举个例子来对比一下两个版本的区别:

document.body.onclick= debounce(function () { console.log('hello') },1000)

如上代码中,我们给body添加了一个点击事件监听器。

  • 如果是版本1的防抖函数,当我点击body时,控制台不会立即打印hello,要等 1000ms 后才会打印。在这 1000s 内如果还点击了 body,那么就会重新计时。即最后一次点击 body 过1000ms后控制台才会打印hello
  • 如果是版本2的防抖函数,当我首次点击body时,控制台会立马打印 hello,但是在此之后的 1000ms 内点击 body ,控制台不会有任何反应。在这 1000s 内如果还点击了 body,那么就会重新计时。必须等计时结束后再点击body,控制台才会再次打印 hello

1.2 防抖的实际应用

(1)搜索框建议项

通常,搜索框会提供下拉菜单,为用户当前的输入提供自动完成选项。但有时建议项是通过请求后端得到的。可以在实现提示文本时应用防抖,在等待用户停止输入一段时间后再显示建议文本。因此,在每次击键时,都会等待几秒钟,然后再给出建议。

(2)消除resize事件处理程序的抖动。

window 触发 resize 的时候,不断的调整浏览器窗口大小会不断触发这个事件,用防抖让其只触发一次

(3)自动保存

例如掘金一类的网站,都会内嵌文本编辑器,在编辑过程中会自动保存文本,防止数据丢失。每次保存都会与后端进行数据交互,所以可以应用防抖,在用户停止输入后一段时间内再自动保存。

(4)手机号、邮箱等输入验证检测

通常对于一些特殊格式的输入项,我们通常会检查格式。我们可以应用防抖在用户停止输入后一段时间再进行格式检测,而不是输入框中内容发生改变就检测。

(5)在用户停止输入之前不要发出任何 Ajax 请求

二、节流(Throttle)

节流:无论用户触发事件多少次,附加的函数在给定的时间间隔内只会执行一次。(即:回调函数在规定时间内最多执行一次)

 例如,当用户单击一个按钮时,会执行一个在控制台上打印Hello, world的函数。现在,假设对这个函数应用 1000 毫秒的限制时,无论用户点击按钮多少次,Hello, world在 1000 毫秒内都只会打印一次。节流可确保函数定期执行。

2.1 节流函数的实现

(1)版本1 —— 使用定时器

 /**
* @desc 函数节流
* @param func 回调函数 
* @param limit 时间限制
*/
const throttle = (func, limit) => {
    let inThrottle;  // 是否处于节流限制时间内

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

        // 跳出时间限制
        if (!inThrottle) {
            func.apply(context, args);  // 执行回调
            inThrottle = true;  
            // 开启定时器计时
            setTimeout(() => inThrottle = false, limit);
        }
    }
}

(2)版本2 —— 计算当前时间与上次执行函数时间的间隔

 /**
* @desc 函数节流
* @param func 回调函数
* @param limit 时间限制
*/ 
function throttle(func, limit) {
    //上次执行时间
    let previous = 0;
    return function() {
        //当前时间
        let now = Date.now();

        let context = this;
        let args = arguments;

        // 若当前时间-上次执行时间大于时间限制
        if (now - previous > limit) {
            func.apply(context, args);
            previous = now;
        }
    }
} 

很多现有的库中已经实现了防抖函数和节流函数

2.2 节流的实际应用

(1)游戏中通过按下按钮执行的关键动作(例如:射击、平A)

拿王者荣耀为例,通常都有攻速一说。如果攻速低,即使 n 毫秒内点击平A按钮多次,也只会执行一次平A。其实这里就类似于节流的思想,可以通过设置节流的时间间隔限制,来改变攻速。

(2)滚动事件处理

如果滚动事件被触发得太频繁,可能会影响性能,因为它包含大量视频和图像。因此滚动事件必须使用节流

(3)限制mousemove/touchmove事件处理程序

小结

如何选择防抖和节流:

关于防抖函数和节流函数的选择,一篇博客中是这样建议的:

A debounce is utilized when you only care about the final state. A throttle is best used when you want to handle all intermediate states but at a controlled rate.

即:如果只关心最终状态,建议使用防抖。如果是想要函数以可控的速率执行,那么建议使用节流。

延时多久合理:

  • 大多数屏幕的刷新频率是每秒60Hz,浏览器的渲染页面的标准帧率也为60FPS,浏览器每秒会重绘60次,而每帧之间的时间间隔是DOM视图更新的最小间隔。
  • 一个平滑而流畅的动画,最佳的循环间隔即帧与帧的切换时间希望是 16.6ms(1s/60)内,也意味着17ms内的多次DOM改动会被合并为一次渲染。
  • 当执行回调函数时间大于16.6ms(系统屏幕限制的刷新频率),UI将会出现丢帧(即UI这一刻不会被渲染),且丢帧越多,引起卡顿情况更严重。

到此这篇关于JavaScript 防抖debounce与节流thorttle的文章就介绍到这了,更多相关JavaScript 防抖与节流内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

JavaScript 防抖debounce与节流thorttle的更多相关文章

  1. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  2. HTML5数字输入仅接受整数的实现代码

    这篇文章主要介绍了HTML5数字输入仅接受整数的实现代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. amaze ui 的使用详细教程

    这篇文章主要介绍了amaze ui 的使用详细教程,本文通过多种方法给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  4. html5简介_动力节点Java学院整理

    这篇文章主要介绍了html5简介,用于指定构建网页的元素,这些元素中的大多数都用于描述网页内容,有兴趣的可以了解一下

  5. ios 8 Homescreen webapp,关闭和打开iPad停止javascript

    我有一个适用于iPad的全屏HTML5网络应用程序,并且刚刚安装了IOS8来试用它,它一切正常,直到你关闭并重新启动iPad.一旦web应用程序重新启动javascript就会停止并加载新页面不会重新启动它.在iPad上的Safari中打开同一页面时,关闭和打开iPad会继续按预期工作.其他人注意到了这个或想出了一个解决方案吗?解决方法这似乎是我在iOS8.1.1更新中解决的.

  6. iOS 6 javascript与object.defineProperty的间歇性问题

    当访问使用较新的Object.defineProperty语法定义属性的对象的属性时,有没有其他人注意到新iOS6javascript引擎中的间歇性错误/问题?https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty我正在看到javascript失败的情况,说

  7. ios – 如何使用JSExport导出内部类的方法

    解决方法似乎没有办法将内部类函数导出到javascript.我将内部类移出并创建了独立的类,它起作用了.

  8. 静音iOS推送通知与React Native应用程序在后台

    我有一个ReactNative应用程序,我试图获得一个发送到JavaScript处理程序的静默iOS推送通知.我看到的行为是AppDelegate中的didReceiveRemoteNotification函数被调用,但是我的JavaScript中的处理程序不会被调用,除非应用程序在前台,或者最近才被关闭.我很困惑的事情显然是应用程序正在被唤醒,并且它的didReceiveRemoteNotifi

  9. ios – 内存泄漏与UIWebView和Javascript

    清楚地包含一个Javascript文件到我的HTML是使UIWebView泄漏内存.当我重复使用相同的UIWebView对象时,或者每当我有内容实例化一个新的漏洞时,会出现泄漏的事实,导致我认为必须有一些JavaScript文件被loadHTMLString处理,导致泄漏.有人知道如何解决这个问题吗?

  10. iOS应用程序的UI自动化测试如何与乐器和Javascript

    从WWDC2010视频会议中了解iOS应用程序的自动化UI测试,但没有实践.从代码项目project,我们可以有一个例子.这个问题在这里听到有涉及这个的人.任何限制?解决方法我建议从AlexWollmer开始使用thisblogpost.他创建了一个非常有用的JavaScript库:tuneup_jswithtest()函数,它允许测试分离和有用的帮助者以及为自动化仪器编写测试的断言.

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部