在前端开发中,我们会遇到一些需要持续触发的事件或者一些持续的请求,但是我们不希望在事件触发时过于频繁的去执行函数。
所以这个时候,就出现了函数防抖和函数节流的解决方案。
下面通过这样一个场景来说明:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>函数防抖和函数节流</title>
</head>
<body>
<div id="content" style="height:150px;line-height:150px;text-align:center; color: #ff1e1e;background-color:#3cccc7;font-size:80px;"></div>
<script type="text/javascript">
let num = 1;
let content = document.getElementById('content');
function count() {
content.innerHTML = num++;
};
content.onmousemove = count;
</script>
</body>
</html>
在上述代码中,div 元素绑定了 mousemove 事件,当鼠标在div区域中移动的时候会持续地去触发该事件导致频繁执行函数。
可以看到,在没有通过其它操作的情况下,函数被频繁地执行导致页面上数据变化特别快。所以,接下来让我们来看看防抖和节流是如何去解决这个问题的。
函数防抖(debounce)
函数防抖就是在触发事件后 N 秒内函数只能执行一次,如果在 N 秒内又触发了事件,则重新开始计算函数执行时间
函数防抖分为立即执行和非立即执行两种
- 函数防抖--非立即执行
//非立即执行
function debounce(fun, delay) {
let timeout; //需要一个外部变量,为增强封装,所以使用闭包
return function () {
let _this = this;
let arg = arguments; //arguments中存着e
if (timeout) clearTimeout(timeout);
timeout = setTimeout(()=> {
fun.apply(_this,arg);
},delay)
}
}
非立即执行版的意思是触发事件后函数不会立即执行,而是在 n 秒后执行,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
我们依旧使用上述绑定 mousemove 事件的例子,通过上面的防抖函数,我们可以这么使用
content.onmousemove = debounce(count,1000);
可以看到,在触发事件后函数 1 秒后才执行,而如果我在触发事件后的 1 秒内又触发了事件,则会重新计算函数执行时间。
- 函数防抖--立即执行
//立即执行
function debounce(fun, delay) {
let timeout;
return function () {
let _this = this;
let arg = arguments;
if (timeout) clearTimeout(timeout);
let applyNow = !timeout;
timeout = setTimeout(()=> {
timeout = null;
},delay)
if(applyNow) fun.apply(_this,arg);
}
}
立即执行版的意思是触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果。
开发过程中的需求是多样的,为了适应各种需求,我们可以把上面两种情况都封装到一个方法里,根据实际情况选择调用。
function debounce(fun, delay, immediate) {
//immediate为true时立即执行,反之为非立即执行
let timeout;
return function () {
let _this = this;
let arg = arguments;
if (timeout) clearTimeout(timeout);
if(immediate){
//立即执行
let applyNow = !timeout;
timeout = setTimeout(()=> {
timeout = null;
},delay)
if(applyNow) fun.apply(_this,arg);
}else{
//非立即执行
timeout = setTimeout(()=>{
fun.apply(_this,arg);
},delay)
}
}
}
函数节流(throttle)
函数节流是指在连续触发事件时函数在 N 秒内只执行一次,节流会稀释函数的执行频率
一般有两种方式可以实现,分别是时间戳版和定时器版。
- 函数防抖--时间戳版
//时间戳版
function throttle(fun, delay) {
let previous = 0;
return function () {
let _this = this;
let arg = arguments;
let now = Date.now();
console.log(now);
if(now - previous > delay){
fun.apply(_this,arg);
previous = now;
}
}
}
- 函数防抖--定时器版
//定时器版
function throttle(fun, delay) {
let timeout;
return function () {
let _this = this;
let arg = arguments;
if(!timeout){
timeout = setTimeout(()=>{
timeout = null;
fun.apply(_this,arguments);
},delay)
}
}
}