应用场景
- 监听滚动事件(scroll) 频繁触发事件,操作DOM,大量计算
- 鼠标移动事件(mousemove)
- 监视窗口大小 resize事件
- onkeyup事件,获取服务器资源
如果回调函数 涉及大量计算,或者获取服务器资源时,频繁触发会触发会导致响应跟不上,页面卡死、卡顿现象。
针对这种短时间内连续触发、不可控制的问题。可以使用防抖和节流这两个策略来解决。
防抖
定义
当事件被触发时,设定一个周期延迟执行动作,若期间又被触发,则重新设定周期,直到周期结束,执行动作
特点
在事件连续触发时,在周期结束时,只会执行一次
//防抖函数简单实现
//css 部分
#container{
width:400px;
height:200px;
line-height:200px;
text-align: center;
color:#fff;
background-color: #333;
font-size:30px;
}
<div id="container"></div>
<script type="text/javascript">
//减少不必要的计算 不浪费资源 只在恰当的时间计算
var count = 1;
var container = document.getElementById("container");
function getUserAction(){
console.log(this);
container.innerHTML = count++;
}
//container.onmousemove = getUserAction; // 未进行防抖时, 会频繁触发
container.onmousemove = debounce(getUserAction, 1000);
//延迟 debounce (即事件不立即触发,在周期结束时,执行回调函数)
//此方法是在周期的结束边界 执行
function debounce(fn, delay){
let timer;
return function(){
let self = this;
timer && clearTimeout(timer);
timer = setTimeout(() =>{
fn.apply(self);
}, delay);
}
}
//如果需要立即执行函数 即在周期开始时就执行,实现如下
//此方法是在周期的开始边界执行 当immediate 为true时
function debouce(fn, wait, immediate = false){
let timer;
return function(){
var self = this;
timer && clearTimeout(timer);
if(immediate ){
var callNow = !timer;
timer = setTimeout(() =>{
timer= null;
}, wait);
if(callNow){
fn.apply(self);
}
}else{
timer= setTimeout(() =>{
fn.apply(self);
},wait);
}
}
}
</script>
节流
定义
固定周期内,只执行一次动作,若有新事件触发,不执行。周期结束后,又有事件触发,开始新的周期
//节流简单实现
function throttle(fn, delay){
let prev = 0;
return function(){
let self = this;
let now = +new Date(); // + 运算符获取当前时间的时间戳
if(now - prev > delay){
fn.apply(self);
prev = now;
}
}
}
防抖和节流区别
防抖是在某个时间段内只会执行一次,如果间隔时间内再次出发事件,则清除上次定时器,重新开始定时器,只有最后一次操作会被执行。
节流是间隔时间执行,不管触发频率多频繁,都会保证在规定时间内执行一次。