防抖(debounce)
定义:在事件被触发n秒后再执行回调函数,如果在这n秒内又被触发,则重新计时。
用途:在进行窗口的resize、scroll,输入框内容校验或搜索等操作时,如果事件处理函数调用的频率无限制,会加重浏览器的负担,导致用户体验非常糟糕。
举例:都知道的等电梯,例如张三在6:10进入电梯,正常电梯停留5分钟即6:15关门,但在6:13时李四又按了电梯随后走进了电梯,那么电梯会在6:18正常运行。
scroll事件
当持续触发scroll事件时,事件处理函数handle只在停止滚动1000毫秒之后才会调用一次,即在持续触发scroll事件的过程中,事件处理函数handle一直没有执行。
// 防抖函数
function debounceFunc(fn, delay) {
let timeout = null;
return function() {
if(timeout) clearTimeout(timeout);
timeout = setTimeout(fn, delay);
}
}
// 处理函数
function handle() {
console.log(Math.random());
}
// 滚动事件
window.addEventListener('scroll', debounceFunc(handle, 1000));
Input搜索事件
以百度输入框为例,例如想查找“前端进阶”四个关键字,实现输完了“前端进阶”之后,再进行搜索请求,而不是每输入一个字就进行一次请求,这样可以有效减少请求次数,节约请求资源。
<body>
<input id="name" type="text" οnkeyup="ajax">
<script>
// 模拟ajax请求
function ajax(e) {
console.log(e.target.value);
// this.props.ajax.post('/getParams', params);
}
// 防抖函数
function debounceFunc(fun, delay) {
let timer = null;
return function (arguments) {
// 获取函数的作用域和变量
let that = this;
let args = arguments;
// 清除定时器
if(timer) clearTimeout(timer);
timer = setTimeout(function () {
// call方法第一个参数确定新的this指向,后面可以有多个参数是一串参数列表
fun.call(that, args)
}, delay)
}
}
const name = document.getElementById('name');
name.addEventListener('input', debounceFunc(ajax, 2000));
</script>
</body>
节流(throttle)
定义:规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数被执行,如果在同一个单位时间内某事件被触发多次,只有一次会生效。
用途:鼠标连续多次click事件,监听滚动事件,比如是否滑到底部自动加载更多。
举例:还是等电梯,例如张三在6:10进入电梯,正常电梯停留5分钟即6:15关门,6:13时李四又按了电梯随后走进了电梯,但电梯都会在6:15正常运行。
分类:函数节流主要有两种实现方法,时间戳和定时器
时间戳
当高频事件触发时,第一次会立即执行(给scroll事件绑定函数与真正触发事件的间隔一般大于delay)
而后再怎么频繁地触发事件,也都是每delay时间才执行一次;
当最后一次事件触发完毕后,事件也不会再被执行了(最后一次触发事件与倒数第二次触发事件的间隔一定小于delay,因为大于的话就不叫高频事件了)
// 节流函数
function throttle(func, delay) {
let prev = Date.now();
return function() {
let context = this;
let args = arguments;
let now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
定时器
当第一次触发事件时,不会立即执行函数,而是在delay秒后才执行。
而后再怎么频繁触发事件,也都是每delay时间才执行一次。
当最后一次停止触发后,由于定时器的delay延迟,还会执行一次函数。
function throttle(func, delay) {
let timer = null;
return function() {
let context = this;
let args = arguments;
// 如果定时器存在,就不执行,直到delay时间后,定时器执行函数
if (!timer) {
timer = setTimeout(function() {
func.apply(context, args);
// 清空定时器,这样就可以设置下个定时器
timer = null;
}, delay);
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
时间戳 + 定时器
在节流函数内部使用开始时间startTime、当前时间curTime与delay来计算剩余时间remaining。
当remaining<=0时表示该执行事件处理函数了(保证了第一次触发事件就能立即执行事件处理函数,且每隔delay时间执行一次事件处理函数)。
如果还没到时间的话就设定在remaining时间后再触发 (保证了最后一次触发事件后还能再执行一次事件处理函数)。
当然如果在remaining这段时间中如果又一次触发事件,那么会取消当前的计时器,并重新计算一个remaining来判断当前状态。
// 节流throttle代码(时间戳+定时器):
function throttle(func, delay) {
let timer = null;
const startTime = Date.now();
return function() {
const curTime = Date.now();
const remaining = delay - (curTime - startTime);
let context = this;
let args = arguments;
clearTimeout(timer);
if (remaining <= 0) {
func.apply(context, args);
startTime = Date.now();
} else {
timer = setTimeout(func, remaining);
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
总结
原理区分
防抖
维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,都会清除当前的计时器 重新计时。这样只有最后一次操作事件才被真正触发。
节流
通过判断是否到达一定时间来触发函数,若没到规定时间则使用计时器延后,而下一次事件则会重新设定计时器。
用途区分
防抖
input搜索联想,用户在不断输入值时,即在停止输入的几秒后进行搜索,用防抖来节约请求资源。
window触发resize的时候,不断的调整浏览器窗口大小会不断的触发某个事件,用防抖来让其只触发一次。
节流
鼠标不断点击触发(例如点击提交请求按钮),用节流来控制点击或请求的次数。
监听滚动事件,比如是否滑到底部自动加载更多。
防抖 + 节流
处理搜索框,基于性能考虑,肯定不能用户每输入一个字符就发送一次搜索请求。
一种方法就是等用户停止输入,比如过了500ms用户没有再输入,那么就搜索此时的字符串,这就是防抖。
节流比防抖宽松一些,比如我们希望给用户一些搜索提示,所以在用户输入过程中,每过500ms就查询一次相关字符串,这就是节流。
😊 参考链接:segmentfault.com/a/119000001…
😊 刚刚加入到掘金社区,欢迎提出宝贵建议哈,争取每天推送自己整理的笔记,一起进步学习。