防抖和节流是针对响应跟不上触发频率这类问题的两种解决方案。严格算起来防抖和节流应该属于性能优化的知识,但实际上遇到的频率相当高,处理不当或者放任不管就容易引起浏览器卡死。所以还是很有必要早点掌握的。
每日一问:你知道啥是防抖和节流吗???
答: 防抖是当持续触发事件时,从最后一次触发时间起延迟delay秒执行,忽略掉之前的触发,若delay时间未到又触发了,则从这次触发重新计时delay秒执行,以此类推;节流类似水龙头,把水龙头关小,让水一滴一滴往下滴,也就是控制在每delay秒执行一次,在第一次触发时计时delay秒执行,delay秒之中的触发会被忽略掉,在执行之后,再找到执行后的第一次出发,计时delay秒执行,忽略掉delay期间的触发,以此类推。
这个答案还是有些模糊。没关系,看完下面,就完全懂啦!
防抖
当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。如下图,持续触发scroll事件时,并不执行handle函数,当1000毫秒内没有触发scroll事件时,才会延时触发scroll事件。主要应用场景有:input验证、搜索联想、resize
手写防抖函数:
function debounce(fn, wait) {
var timeout = null;
return function() {
if(timeout !== null) clearTimeout(timeout);
timeout = setTimeout(fn, wait);
}
}
// 处理函数
function handle() {
console.log(Math.random());
}
// 滚动事件
window.addEventListener('scroll', debounce(handle, 1000));
//setInterval(),setTimeout() 会返回一个ID,也就是这里的timer,你可以将这个ID传递给clearInterval(),clearTimeout() 以取消执行。
// 这几个函数都是浏览器 window 对象提供的,没有公开的规范和标准,所以并不保证这些ID都是从1开始。
复制代码
节流
当持续触发事件时,保证一定时间段内只调用一次事件处理函数。节流通俗解释就比如我们水龙头放水,阀门一打开,水哗哗的往下流,秉着勤俭节约的优良传统美德,我们要把水龙头关小点,最好是如我们心意按照一定规律在某个时间间隔内一滴一滴的往下滴。如下图,持续触发scroll事件时,并不立即执行handle函数,每隔1000毫秒才会执行一次handle函数。主要应用场景有:scroll、touchmove
手写节流函数:
// 节流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));
// 此时只是注册,注册时执行了整个throttle(handle)函数,return里面是返回的函数代码,所以无需执行return里面的,直接返回就结束throttle了,当触发resize时,由于throttle(handle)代表的是return后面的函数,所以只执行return后面的部分。
复制代码
动手尝试
如果还是不太理解或者抱有疑问,自己尝试一下就好啦~
新建一个html文件,复制以下代码,保存后再拖到浏览器,F12打开控制台,就可以自己动手尝试啦!
大家可以根据控制台输出的毫秒数,彻底理解一下防抖和节流。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="redBox">防抖点击测试,间隔:1000s</div>
<div class="redBox">节流点击测试,间隔:1000s</div>
</body>
<script>
document.getElementsByClassName("redBox")[0].addEventListener('click',debounce(fn,1000));
document.getElementsByClassName("redBox")[1].addEventListener('click',throttle(fn1,1000));
function debounce(fn,delay){
let timer=null;
return function(){
if(timer!==null){
clearTimeout(timer);
}
timer=setTimeout(fn,delay);
}
}
function fn(){
console.log('debounce测试',new Date().getTime())
}
function fn1(){
console.log('throttle测试',new Date().getTime())
}
function throttle(fn,delay){
let timer=null;
return function(){
if(timer!==null){
return;
}
timer=setTimeout(()=>{
fn();
timer=null;
},delay);
}
}
</script>
<style>
.redBox{
width: 300px;
height: 300px;
border: 1px solid red;
margin: 30px;
line-height: 300px;
text-align: center;
}
</style>
</html>
复制代码