概念
函数防抖和函数节流,两者都是优化高频率执行js代码的一种手段。
防抖:就是一定时间内,只会执行最后一次任务(是将多次执行变为最后一次执行)
节流:就是一定时间内,只执行一次 ;(节流是将多次执行变为一定时间内只执行一次)
函数防抖
函数防抖(debounce
):当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。
函数防抖通过设置
setTimeout
定时器的方式延迟执行,当快速多次点击的时候,每一次都会重置定时器,只有你一段时间都不点击时定时器才能到达条件并执行事件函数。即如果触发事件后在 n 秒内又触发了事件,则会重新计算函数延执行时间。
案例1
<button id="debounce">点我防抖!</button>
<script>
$('#debounce').on('click', debounce());
function debounce() {
//设置time为定时器
let timer = null;
return function () {
//如果定时器存在则清空定时器
if(timer) {
clearTimeout(timer);
}
//设置定时器,规定时间后执行真实要执行的函数
timer = setTimeout(() => {
// 需要防抖的操作...
console.log("防抖成功!");
}, 1000);
}
}
</script>
当持续点击button事件时,事件处理函数在1000毫秒之前会清除定时器,也就是说在持续点击btton事件的过程中,事件处理函数一直没有执行。
案例2
// 防抖
function debounce(func, wait=0) {
if (typeof func !== 'function') {
throw new TypeError('need a function arguments')
}
let timer = null;
let result;
return function() {
let context = this;
let args = arguments;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function() {
result = func.apply(context, args);
}, wait);
return result;
}
}
// 处理函数
function handle() {
console.log(Math.random());
}
// 滚动事件
window.addEventListener('scroll', debounce(handle, 1000));
当持续触发scroll
事件时,事件处理函数handle
只在停止滚动1000毫秒之后才会调用一次,也就是说在持续触发scroll
事件的过程中,事件处理函数handle
一直没有执行。
使用场景
函数防抖的应用场景,最常见的就是页面滚动条监听的例子,来进行解析:
let timer;
window.onscroll = function () {
clearTimeout(timer);
timer = setTimeout(function () {
//滚动条位置
let scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
console.log('滚动条位置:' + scrollTop);
}, 200)
}
防抖函数的封装使用
// 防抖函数
function debounce(fn,delay) {
//设置time为定时器
let timer;
// 使用闭包,多次调用都能访问到同一个变量,而不是生成新的变量
return function () {
//如果定时器存在则清空定时器
if(timer) {
clearTimeout(timer);
}
//设置定时器,规定时间后执行真实要执行的函数
timer = setTimeout(() => {//此箭头函数里的this指向btn这个按钮
//改变真实要执行函数的this指向,原submit函数里面的this指向window
fn.apply(this,arguments);
},delay);
}
}
window.onscroll = debounce(function () {
let scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
console.log('滚动条位置:' + scrollTop);
},200)
// jq
$(window).on('scroll', debounce(function () {
console.log("防抖成功!");
}, 200))
函数节流
函数节流(throttle
):当持续触发事件时,保证一定时间段内只调用一次事件处理函数。
简单来说,就是你一直狂点不停的话,它会每隔一定时间就执行一次。它与防抖最大的区别就是,无论事件触发多么频繁,都可以保证在规定时间内可以执行一次执行函数。
案例1
<button id="throttle">点我节流!</button>
<script>
$('#throttle').on('click', throttle());
function throttle(fn) {
let flag = true;
// 使用闭包,方法多次调用都能访问到同一个变量,而不是生成新的flag变量
return function () {
if (!flag) { return; }
flag = false;
setTimeout(() => {
console.log("节流成功!");
flag = true;
}, 1000);
};
}
</script>
当持续点击button
事件时,事件处理函数在1000毫秒之前变量flag
为false
不会执行下一次定时器。也就是说在持续点击btton
事件的过程中,1000毫秒内不会只调用一次处理函数。
案例2
// 节流throttle代码(定时器):
var throttle = function(func, delay) {
var timer = null;
return function() {
var context = this;
var args = arguments;
if (!timer) {
timer = setTimeout(function() {
func.apply(context, args);
timer = null;
}, delay);
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
当触发事件的时候,我们设置一个定时器,再次触发事件的时候,如果定时器存在,就不执行,直到delay时间后,定时器执行执行函数,并且清空定时器,这样就可以设置下个定时器。当第一次触发事件时,不会立即执行函数,而是在delay秒后才执行。而后再怎么频繁触发事件,也都是每delay时间才执行一次。当最后一次停止触发后,由于定时器的delay延迟,可能还会执行一次函数。
使用场景
函数节流应用的实际场景,常见的时根据文本框中输入的内容自动请求后台数据。
<input type="text" value="" id="input">
<script>
// 节流函数
function throttle(fn, delay) {
let flag = true;
return function () {
if (!flag) { return; }
//防止一直执行
flag = false;
setTimeout(() => {
//到规定时间后执行函数,同时flag=true
fn.apply(this, arguments);
flag = true;
}, delay);
}
}
$('#input').on('keyup', throttle(function () {
console.log($(this).val());
// ajax后台请求....
}, 1000));
</script>
总结
一般当我们提交表单时使用防抖。
但在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 ajax
请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。