1. 场景,是前端做性能优化限制事件触发频率的一种常用手段,比如控制搜索词条的触发频率,也是面试常考题。
2. 概念:事件只在触发的n秒内未再次触发才执行,否则不执行。
3. 实现:从概念来理解,要n秒后才触发这里需要用到定时器setTimeout, n秒内再次触发则不执行这里需要清除每次触发生成的定时器。因为每次都会触发事件,所以需要一个变量存储每次触发的定时器作更新,该变量要在事件触发过程中长期使用,所以考虑用闭包来读取这个变量。
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="IE=edge, chrome=1">
<title>debounce</title>
<style>
#container{
width: 100%; height: 200px; line-height: 200px; text-align: center; color: #fff; background-color: #444; font-size: 30px;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
var count = 1;
var container = document.getElementById('container');
function getUserAction() {
container.innerHTML = count++;
};
container.onmousemove = getUserAction;
</script>
</body>
</html>
以上代码表明div盒子中鼠标移动多少次就执行多少次数字累加,频率非常快。
根据定义实现
function debounce(func, delay) {
let timeout = null;
return function(){
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(this, arguments)
}, delay)
}
}
调用
container.onmousemove = debounce(getUserAction, 1000);
触发onmousemove事件,返回匿名函数执行,先清除上一次触发的定时器,同时将新的定时器存储到timeout中,在delay毫秒后执行累加事件,如果再次触发了onmousemove则会再次执行匿名函数清除上次的定时器,并设置新的定时器存到timeout中,delay毫秒后执行。这里的关键点是onmousemove执行的是返回的匿名函数(闭包),将局部变量timeout一直能够持续读取并更新。
加上立即执行参数
function debounce(func, delay, immediate) {
let timeout = null;
return function () {
clearTimeout(timeout)
if (immediate) {
// callNow为是立即执行的参数
let callNow = !timeout;
console.log(timeout, 'timeout')
timeout = setTimeout(() => {
timeout = null;
}, delay)
if (callNow) {
func.apply(this.arguments)
}
} else {
timeout = setTimeout(() => {
func.apply(this, arguments)
}, delay)
}
}
}
是否立即执行的这段理解,callNow是否立即执行参数,设置定时器在delay毫秒后重新设置timeout为null,只有timeout为null才能执行,因此初次触发后,后面delay毫秒内每次触发事件返回的闭包内对timeout的辅助都是新的定时器,所以callNow必为false,执行不了