JavaScript 防抖与节流理解
当我们用鼠标拖拽窗口,或者其他DOM元素时, 滚动滚动条,频繁点击按钮,鼠标移动时,都会频繁地调用绑定在这些事件上的回调函数,如果回调函数过于复杂就会导致响应跟不上触发,这就可能会造成页面的卡顿,同时高频地触发极大的浪费了资源,降低了性能
为了解决这些问题,因此出现了防抖和节流
1、防抖 ( debounce )
概念: 在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则计时器需要重新计时(即计时器清零, 再过n秒后才能执行回调)
举个栗子~
当鼠标在矩形中移动时,矩形中的数字会增加
代码实现:
<!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>
<style>
#box1{
width: 300px;
height: 100px;
background-color: rgb(157, 156, 156);
margin: 100px auto;
display: flex;
justify-content: center;
align-items: center;
}
#num{
color: white;
font-size: 25px;
}
</style>
</head>
<body>
<div id="box1">
<div id="num">
0
</div>
</div>
<script>
let n = 0;
let box1 = document.getElementById('box1');
let num = document.getElementById('num');
box1.addEventListener('mousemove',function(){
num.innerHTML = ++n;
},false);
</script>
</body>
</html>
可以知道,当鼠标移入到矩形范围内时,每一次移动都会频繁地触发函数;而当函数过于复杂,执行一次开销特别大时,这会造成页面卡顿,用户体验感会变得特别差。
现在我们来对它做防抖处理
<script>
let n = 0;
let box1 = document.getElementById('box1');
let num = document.getElementById('num');
box1.addEventListener('mousemove',debounce(numAdd,1000),false);
function numAdd(){
num.innerHTML = ++n;
}
function debounce(fun,wait){
var timer = null;
return function(){
var context = this;
if(timer){
clearTimeout(timer);
timer = null;
}
timer = setTimeout(()=>{
fun.apply(context);
},wait);
}
}
</script>
做了防抖之后,可以看到,鼠标移入矩形区域后移动,数字没有发生变化,当鼠标移开之后,数字变化了
为什么会变成这样呢?
从代码中可以看到,每一次鼠标移动时都会触发事件,然后执行回调函数,在回调函数中会先对计时器进行判断,如果存在计时器,则清除当前计时器,重新计时,直到鼠标最后一次在矩形区域内移动后触发事件,然后等待wait时间后才执行,使数字加一
主要应用场景:
(1)scroll事件滚动触发, (2)搜索框输入 (3)表单验证提交 (4)按钮提交事件 (5)浏览器窗口缩放,resize事件
2、节流(throttle)
概念:在规定的一定时间内,只能有一次触发事件的回调函数被执行(不管在规定时间内,事件被触发多少次,只能执行一次事件的回调函数)
还是上面那个栗子~
把它改成节流实现:
<script>
let n = 0;
let box1 = document.getElementById('box1');
let num = document.getElementById('num');
box1.addEventListener('mousemove',throttle(numAdd,1000),false);
function numAdd(){
num.innerHTML = ++n;
}
function throttle(fun,wait){
var timeout = null;
return function(){
let context = this;
if(!timeout){
timeout = setTimeout(()=>{
fun.apply(context);
timeout = null;
},wait)
}
}
}
function throttle(fun,wait){
var preTime = Date.now();
return function(){
let context = this;
var nowTime = Date.now();
if(nowTime - preTime >= wait){
preTime = Date.now();
return fun.apply(context);
}
}
}
</script>
做了节流之后,可以看到鼠标在矩形里移动,矩形里的数字会按照设置的时间加1,
为什么会这样呢?
从代码中可以知道,每移动一下鼠标,在规定时间内会触发一次事件,然后执行回调函数;在回调函数中会对计时器进行判断,如果不存在计时器,则在规定时间后执行函数,矩形内数字加1,然后将计时器清除。
主要应用场景:
(1)DOM元素的拖拽功能实现 (2)射击游戏类 (3)计算鼠标移动的距离 (4)监听scroll事件
3、防抖和节流的区别:
防抖和节流都是防止某一时间频繁触发,但是它们的原理不一样
防抖是在某一段时间内执行一次,节流是在间隔相同时间内执行
对于有停顿的高频触发事件选择防抖,对于高频触发且连续的事件选择节流
参考文章:
JS的防抖(debounce)与节流(throttle)
前端——节流、防抖(通俗易懂)
前端节流、防抖
防抖节流基本原理及实现
|