防抖和节流一直都是 前端面试中比较常问的问题
那究竟是什么是防抖 什么节流呢
首先 我们先来谈谈 防抖吧 举一个小例子 大家都知道 照相机吧 如果拍照的时候 突然抖动一下 是不是特别影响拍照效果 其实在项目中有类似这样的问题 比较影响用户的体验
防抖: 在事件触发之后 在特定的时间内 只能执行一次 如果再次触发 时间将会重新计时
比如是 2s 的时间 在 1.5s重新触发下 那么将重新计算2s的时间 执行事件
我们用一个实际的小例子 来说:
在页面上创建一个div 高200px 铺满屏幕的宽度
div{
width: 100%;
height: 200px;
background-color: #999;
text-align: center;
font-size: 50px;
line-height: 200px;
}
<div id="d1">0</div>
let num=0;
function add(){
div.innerHTML=num++;
}
d1.onmousemove=add;
我们给div绑定了一个事件 只要 鼠标在上面移动 就会让上面数字 +1
如果不加和节流的话 只要我们在上面移动 数字就会出现疯狂的+1 甚至快的我们都看不见 加的数字
因为 只要鼠标哪怕是轻微动一下 就会触发一下 可以想象一下 这样 显然 给用户的体验不是太好
这个时候 就可以用到我们的防抖了 设置每次 +1的 时间间隔 2s 若鼠标滑动 那么将重新计时 每次用户鼠标停下来 它才会 +1 要不只要用户在移动鼠标 他就不会 +1
不过这样的情况也分为俩种:
1.一种是 事件触发后俩秒执行
function fangdou1(func,wait){
let timeout;
return function(){
if(timeout) clearTimeout(timeout);
timeout=setTimeout(function(){
func.apply(this); // 保证内外 this 指向一直 func 是 windw对象下的自定义函数
},wait)
}
}
d1.onmousemove=fangdou1(add,2000); // 传入要执行的操作的自定义函数 第二个参数时间间隔
2.另一种是 触发事件后 立即执行 等待俩秒后再执行 下一次操作
function fangdou2(func,wait){
let timeout=false;
return function(){
if(timeout) clearTimeout(timeout);
let canMove=!timeout; // 类型转换
timeout=setTimeout(function(){
timeout=false;
},wait)
if(canMove) func.call(this);
}
}
d1.onmousemove=fangdou2(add,2000); // 传入要执行的操作的自定义函数 第二个参数时间间隔
第一种情况比较常见 第二种情况 也有需要 但比较少
我们先定义一个开关 canMove
只要 canMove 是 true 他才执行函数
但这个时候注意 canMove=!timeout 只要当timeout=false 的时候
canMove 才为true
这个时候给大家复习个知识点
一个定时器对象 的布尔类型是true 即使 被清除了定时器他也是true
也就是说 只要一个对象被赋值成布尔对象 就是true
如: var a=setTimeout(()=>{},1000)
console.log(a==true) // 打印出 true
clearTimeOut(a) console.log(a==true) // 打印出来还是true
第一次 他会立即执行 因为 timeout的初值是 false
如果第二次再让它 +1 这个时候 timeout=true 被赋值了定时器对象了 那么canMove=!timeout canMove=false
这个时候 只要定时器里面的 timeout=false 没有执行的话 那么 canMove 就不可能等于 true
需要 用户 鼠标一动 他就会立即+1 但是下次 必须再等俩秒之后 再动 才会+1
上面的俩种情况根据具体情况我们进行取舍
3.好了 我们下面讲讲 节流的作用
其实 听到这个名字我想到的就是 节省流量 不知道 你们是不是呢
其实在真实的情况下 也确实有节省流量的作用 比如 在网速不好的情况下 用户不停的点击往后台发送请求
这里 节流 也有俩种情况 首先我们先来了解下 节流的概念
节流: 在连续发生的事件中,在特定的时间内只能执行一次
听了概念之后是不是和防抖很像 但不同的是 节流是连续触发事件 时间并不会重新计算
3.1 使用定时器 做节流
不管用户怎么动鼠标 始终是俩秒内执行一次 +1 当用户暂停的时候 也将会暂停+1
相当于 定义一个开关 timeout=false
第一次 (!timeout)=true 的时候设置定时器 timeout=setTimeout执行程序 此时timeout=true
等俩秒后 将 timeout改为false的时候才执行下次 +1
function jieliu1(func,wait){
let timeout=false;
return function(){
if(!timeout){
timeout=setTimeout(function(){
timeout=false;
func.call(this);
},wait)
}
}
}
d1.onmousemove=jieliu1(add,2000); // 传入要执行的操作的自定义函数 第二个参数时间间隔
3.2,上面的程序 在web页面还行 在 移动端效果可能不太好 在移动端 我们可以采用 时间戳的方法进行设置节流
利用俩次时间戳的差值为两秒 执行一次程序 设置 pre 初始值为0 第一次触发事件 是肯定执行的
执行完之后 将 获取到的时间戳 赋值给pre 那么下一次 获取到时间戳 next-pre>2000的时候才会执行程序
这种效果更好
function jieliu2(func,wait){
let pre=0;
return function(){
let next=Date.now();
console.log(next,pre);
if((next-pre)>=wait){
func.call(this);
pre=next;
}
}
}
d1.onmousemove=jieliu2(add,2000); // 传入要执行的操作的自定义函数 第二个参数时间间隔
上面运用到了 闭包得知识 封装的 防抖节流事件 希望能给大家带来帮助 如果觉得有用的话点个赞
上面 call apply 和 bind 更改 this 当前作用域 不太了解的童鞋 可以看我之前的博客
https://blog.csdn.net/yunchong_zhao/article/details/104439466