JavaScript防抖与节流笔记
JavaScript防抖与节流
概念
防抖(debounce)与节流(throttle)是两个相似但有本质区别的两个概念,但两个概念的存在都是为了控制在特定条件下函数最大的执行次数。这在例如将函数执行onScroll
事件绑定这类事件发生次数过多导致回调函数在任务队列积压、回调函数执行时间过长导致调用栈阻塞容易造成前端性能瓶颈时尤为重要,onScroll
事件在拖动滚动条或在手机页面滑动时会发生30-100次,此时如果回调函数内有略微影响性能的函数执行,这个效果会被放大很多倍。这时防抖与节流处理显得尤为重要。
防抖 Debounce
防抖(Debounce)是将某段时间或某两个特定事件之间的多个调用合为一次调用的操作,如果在规定时间内没有调用,则将前面的多个连续调用合为一个执行。
每个竖线分割的为时间单元,着色为在该时间单元内有事件发生,上方是事件处理前的发生情况,下方是防抖处理后的情况。可以看到防抖处理在事件停止连续执行某一时间段后将发生过的连续多个事件合成为一个事件。
前缘防抖 Leading Edge
前缘防抖(Leading Edge 或 Immediate),是将防抖事件的发生放在事件开始时的一种改良。事件发生时前缘防抖立刻放出一个对应事件,后续连续放生的事件将被前缘防抖过滤,直到事件发生间隔大于设定时间后事件再次发生,节流函数将按照相同方式处理事件。
可以看到前缘防抖会在事件发生时先立刻执行,之后将进行前缘防抖操作。
Lodash的防抖
JavaScript库Lodash包含_.debounce(function, [wait=0], [options={}])
函数可以实现防抖function
为需要防抖的函数,wait
是可选的延迟毫秒数,option.leading
标记是(true
)否(false
)使用前缘防抖这个值默认为false
,option.trailing
标记是否使用默认防抖这个值默认为true
,options.maxWait
标记函数最大延迟时间。
_.debounce(sendEmail, 400, {
leading: true,
trailing: false,
maxWait: 1000
})
实际使用的例子
// 避免窗口在变动时出现昂贵的计算开销。
$(window).on('resize', _.debounce(calculateLayout, 150));
// 当点击时 `sendMail` 随后就被调用。
$(element).on('click', _.debounce(sendMail, 300, {
'leading': true,
'trailing': false
}));
// 确保 `batchLog` 调用1次之后,1秒内会被触发。
var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
var source = new EventSource('/stream');
$(source).on('message', debounced);
// 取消一个 trailing 的防抖动调用
$(window).on('popstate', debounced.cancel);
// 左div会在每次浏览器发出resize时打印,右div会在400ms内无resize事件发出时打印
$(document).ready(function(){
var $win = $(window);
var $left_panel = $('.left-panel');
var $right_panel = $('.right-panel');
function display_info($div) {
$div.append($win.width() + ' x ' + $win.height() + '<br>');
}
$(window).on('resize', function(){
display_info($left_panel);
});
$(window).on('resize', _.debounce(function() {
display_info($right_panel);
}, 400));
});
仅安装Lodash的debounce和throttle功能的方法
npm i -g lodash-cli
lodash include = debounce, throttle
防抖的实现
自行实现防抖与节流其实并不难,只要利用好setTimeout
就可以了
function debounce(method, arguments, ctx, time) {
if (typeof method.tId === "undefined") {
method.tId = 0;
method.call(ctx, ...arguments);
}
if (method.tId) {
clearTimeout(method.tId);
}
method.tId = setTimeout(() => {
method.tId = undefined;
}, time || 500);
}
节流 Throttle
节流(Throttle)的目的是在某段时间内允许某个方法执行一次,这与防抖的间隔指定之间分割连续调用段并在这一段的前或后执行一次方法不同,效果就是节流会保证在某段时间内函数将得到执行机会,而防抖则会将只要是按规定时间连续的不论多久或多少次都会将某方法防抖为一次执行。
简单来说就是,节流是根据距离上次执行被节流函数间隔的时间对调用进行过滤,防抖是根据距离上次调用的时间对调用进行过滤。
节流的实现
首先是前缘节流(不知道有这种说法没)。每一次合理的点击(距离上次执行节流后函数的时间大于time或500ms)都将立即执行,否则不执行
/**
* 将一个函数调用包装为节流调用
* @param {function} method 被包装的方法
* @param {array} arguments 调用方法传入的参数
* @param {object} ctx 上下文
* @param {number} time 节流的限制时间
**/
function throttling(method, arguments, ctx, time) {
if (typeof method.tId === "undefined") { // 节流标记位
method.tId = 0;
method.call(ctx, ...arguments);
method.tId = setTimeout(() => {
method.tId = undefined;
}, time || 500);
}
}
再是后缘节流。第一次点击节流后函数将被立即执行,但后续操作只会每time或500ms之后执行一次,也就是后续操作即使在合理频率下也会有延迟
function throttling(method, arguments, ctx, time) {
if (typeof method.tId === "undefined") { // 节流标记位
method.tId = 0;
method.call(ctx, ...arguments);
return;
}
let tId = method.tId;
if (!tId) {
method.tId = setTimeout(() => {
method.call(ctx, ...arguments);
method.tId = 0;
}, time || 500);
}
}
顺便提一下上述两个函数的使用方法。下方是一个代码片段, 实现当页面按钮点击时在上方获取到的ul
元素上添加一个li
元素,li
元素内部标记有当前addUlElement
函数触发时按钮的点击的次数。可以看到我们使用throttling()
对点击事件进行了包装,当click
事件触发时我们不直接调用addUlElement()
函数本体,而是将调用使用节流方法过滤频率过高的调用,然后以合适的频率执行函数。
function addUlElement(num) {
var li = document.createElement("li");
li.innerHTML = "这是第 " + num + " 次点击按钮产生的li";
ul.appendChild(li);
}
button.addEventListener("click", () => {
throttling(addUlElement, [++num], this, 1000);
});
rAF requestAnimationFrame
实现执行速率限制的另一种方法是使用requestAnimationFrame
,它为60fps的浏览器界面渲染的每帧绑定,它的效果与_.throttle(function, 16)
效果差不多,但是可靠性和性能都更好,因为它是微任务且是浏览器原生API。在滚动、鼠标、键盘事件配合调整元素位置或大小、动画时使用该函数是一个优先选择。
rAF的缺点主要来源于IE9、Opera Mini和一些旧安卓浏览器没有该函数支持,Nodejs也不支持该函数
下面是一个使用rAF的实例,我们使用rAF实现每一帧执行一次动画,如果使用循环则会阻塞且执行结果与速度将不可预期,如果使用setTimeout
或setInterval
又没有办法精准控制执行次数导致动画并不细腻流畅且使用这两个函数执行动画性能较低。
function step(timestamp) {
if (start === undefined)
start = timestamp;
const elapsed = timestamp - start;
//这里使用`Math.min()`确保元素刚好停在200px的位置。
element.style.transform = 'translateX(' + Math.min(0.1 * elapsed, 200) + 'px)';
if (elapsed < 2000) { // 在两秒后停止动画
window.requestAnimationFrame(step);
}
}
window.requestAnimationFrame(step);
总结
- 防抖 debounce:将一段连续多次的调用合成为一个,默认之后执行,前缘防抖将立即执行
- 节流 throttle:保证每段时间内至少执行一次调用,可以用于检查、用户操作等
- rAF:一个节流的更高性能的替代品,最好用于UI相关任务,但不支持IE9
JavaScript防抖与节流笔记的更多相关文章
- JavaScript 防抖和节流
1. 概述 1.1 说明 在项目过程中,经常会遇到一个按钮被多次点击并且多次调用对应处理函数的问题,而往往我们只需去调用一次处理函数即可.有时也会遇到需要在某一规则内有规律的去触发对应的处理函数,所以 ...
- 来聊聊JavaScript中的防抖和节流
目录 JavaScript防抖和节流 问题还原 防抖 什么是防抖 使用场景 节流 什么是节流 使用场景 JavaScript防抖和节流 问题还原 我们先来通过代码把常见的问题还原: <html& ...
- 原生JavaScript实现函数的防抖和节流
原生JavaScript实现函数的防抖和节流 参考:https://www.jianshu.com/p/c8b86b09daf0 想详细了解的直接戳上面链接了,讲得非常清楚.下面只给代码和我自己写的注 ...
- JavaScript:防抖与节流
①防抖: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <titl ...
- 彻底搞懂JavaScript的闭包、防抖跟节流
最近出去面试了一下,收获颇多!!! 以前的我,追求实际,比较追求实用价值,然而最近面试,传说中的面试造火箭,工作拧螺丝,竟然被我遇到了.虽然很多知识点在实际工作中并不经常用到,但人家就是靠这个来筛选人 ...
- JavaScript 中的防抖和节流
什么是防抖 函数防抖(debounce):当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时.如下图,持续触发 scrol ...
- javascript之防抖与节流
防抖 你是否在日常开发中遇到一个问题,在滚动事件中需要做个复杂计算或者实现一个按钮的防二次点击操作. 这些需求都可以通过函数防抖动来实现.尤其是第一个需求,如果在频繁的事件回调中做复杂计算,很有可能导 ...
- JS的防抖与节流学习笔记
防抖(debounce):当持续触发事件时,在一定的时间段内,只有最后一次触发的事件才会执行. 例: function debounce(fn, wait) { var timer = null; r ...
- JavaScript中函数防抖、节流
码文不易,转载请带上本文链接,感谢~ https://www.cnblogs.com/echoyya/p/14565642.html 目录 码文不易,转载请带上本文链接,感谢~ https://www ...
- JavaScript中的防抖与节流-图文版
01.防抖还是节流 防抖 与 节流 目的都是避免一定时间内,大量重复的操作造成的性能损耗.因此原理也类似,都是阻止过多的事件执行,只保留一部分来执行.适用场景略有不同,也有交叉,动手练习一遍就懂了. ...
随机推荐
- SpringBoot项目实现日志打印SQL明细(包括SQL语句和参数)几种方式
前言 我们在开发项目的时候,都会连接数据库.有时候遇到问题需要根据我们编写的SQL进行分析,但如果不进行一些开发或者配置的话,这些SQL是不会打印到控制台的,它们默认是隐藏的.下面给大家介绍几种常用的 ...
- github 解决推拉代码提示 REMOTE HOST IDENTIFICATION HAS CHANGED 失败
本文记录最近 github 推送或拉取代码时提示 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! 而失败的解决方法 报错提示如下 @@@@@@@@@@ ...
- WPF 如何获取有哪些 VisualBrush 用了某个控件
我写了一个特殊的控件,我期望了解到有哪些 VisualBrush 捕获了此控件,或者说有哪些 VisualBrush 用了此控件的界面 本文的方法需要用到反射,需要使用 WPF 框架里面没有公开的字段 ...
- 2019-10-14-云之幻-UWP-视频教程
title author date CreateTime categories 云之幻 UWP 视频教程 lindexi 2019-10-14 21:8:26 +0800 2019-10-14 21: ...
- VSCode 打开ESP32工程问题
一.无法跳转 问题现象: 打开ESP32工程头文件提示波浪线不跳转,如下图所示: 解决办法: 删除工程中.vsccode文件夹下的所有文件 VSCode 中打开命令行搜索 ESP-IDF 找到`添加 ...
- HarmonyOS 鸿蒙隔离层设计
在软件开发中,底层库的更换或升级是常见的需求,这可能由性能提升.新功能需求或安全性考虑等因素驱动.为了降低迁移成本,良好的设计模式至关重要. 在版本迭代过程中,网络请求库可能会经历从A到B再到C的演进 ...
- Multisim仿真验证之二极管的特性参数
二极管的特性 正向 R1 10% 20% 30% 50% 70% 90% Vd/mV 299 543 583 608 627 658 Id/mA 0.01 0.1 0.6 1.4 2.8 7.2 rd ...
- 自动生成robot自动化测试用例
背景:java项目使用swagger管理接口,随着需求的开发接口也有增加,要从swagger界面中去查找出新增的接口是件很费时,效率很低的事情. 适用情况: java项目且适用swagger管理接口 ...
- 创建第一个springmvc程序
创建第一个springmvc程序 1.创建父项目文件,导入依赖,删除src文件夹 pom.xml文件 <dependencies> <dependency> <group ...
- WEB服务与NGINX(26)- 实现Nginx高并发系统内核参数优化
1. 实现Nginx高并发系统内核参数优化 由于默认的Linux内核参数考虑的是最通用场景,这明显不符合用于支持高并发访问的Web服务器的定义,所以需要修改Linux内核参数,使得Nginx可以拥有更 ...