在前端开发中会遇到一些频繁的事件触发,例如input,keyup,keydown,scroll,resize,mousemove等,这非常影响性能,所以我们需要控制它们触发的频率,方法就是防抖与节流。
函数防抖(debounce)
定义:当持续触发事件时(如连续点击按钮多次),一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,有一次触发了事件,就重新开始延时。
防抖:按照固定的
频率
执行,比如 3 秒执行一次Input事件,如果再3秒前之前就触发Input,那么我们清除计时器,从刚刚Input的时候再次计时,再3秒后触发;
原理:维护一个计时器,规定在延时时间后触发函数,但是在延时时间内再次被触发的话,就取消之前的计时器而重新设置,这样就能够保证只有最后一次操作被触发。即将所有操作合并为一个操作进行,并且只有最后一次操作是有效操作。
应用场景:在项目中,我有一个需要实时搜索并出结果的操作,简单的说,就是在一个input上绑定了keyup事件,在执行keyup时在里面设置一个定时器,因为一般输入比时间要快,前面的keyup里面的事件就被clear,然后再接着执行后面的搜索。也就实现了简单的防抖操作
函数节流(throttle)
定义:按照设定的固定时间执行一次函数,比如200ms一次。注意:固定就是你在mousemove过程中,执行这个节流函数,它一定是200ms(你设定的定时器延迟时间)内执行一次。没到200ms,一定会返回,不会执行回调函数的。
节流:按照固定的时间执行,没到时间,则不执行,到时间才执行,通过一个开关来控制。
原理:原理是通过判断是否达到一定时间来触发函数,使得一定时间内只触发一次函数
函数防抖代码实现:
触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间
每次触发事件时都取消之前的延时调用方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>防抖</title>
</head>
<body>
<input type="text" id="txt_id">
<script>
function debounce(fn) {
let timer = null
console.log(this)
return function () {
clearTimeout(timer)
console.log('aaa',this)
timer = setTimeout(()=>{
console.log('bb',this)
fn.call(this)
}, 2000)
}
}
function say () {
console.log('hello word')
}
Oinput = document.getElementById('txt_id')
Oinput.addEventListener('input', debounce(say))
</script>
</body>
</html>
函数节流代码实现:
高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率
每次触发事件时都判断当前是否有等待执行的延时函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>节流</title>
<style>
h1 {
background: skyblue;
}
</style>
</head>
<body>
<h1 id="h1_id">0</h1>
<script>
function say(e) {
let ev = e || event
ev.target.innerText = count++
}
function throttle(fn) {
console.log(fn)
let canRun = true
return function () {
if (!canRun)
return
canRun = false
setTimeout(() => {
fn.apply(this, arguments)
canRun = true
}, 100)
}
}
let count = 0
oH1 = document.getElementById('h1_id')
oH1.addEventListener('mousemove', throttle(say))
</script>
</body>
</html>
参考链接:http://www.52codes.net/develop/javascript/59224.html
<!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>debounce</title>
<style>
#main {
width: 300px;
height: 300px;
background-color: #ccc;
}
</style>
</head>
<body>
<div id="main"></div>
</body>
<script>
// function debounce(fn) {
// let timer = null;
// return function() {
// clearTimeout(timer);
// timer = setTimeout(() => {
// fn();
// }, 1000)
// }
// }
// function throttle(fn) {
// let canRun = true;
// console.log(11)
// return function() {
// if (!canRun) {
// return false;
// }
// canRun = false;
// setTimeout(() => {
// fn();
// canRun = true;
// }, 1000)
// }
// }
// function position() {
// console.log(1);
// }
// const area = document.getElementById('main');
// area.addEventListener('mousemove', position);
// area.addEventListener('mousemove', debounce(position));
// area.addEventListener('mousemove', throttle(position));
const div = document.querySelector('#main');
// 防抖:按照一定频率进行,和时间轴无关
const debounce = (fn) => {
let timer;
return function() {
clearTimeout(timer); // 你一动,我就给你取消掉
timer = setTimeout(() => {
fn();
}, 1000);
}
}
// 节流:按照某一时间频率运行
const throttle = (fn) => {
let canRun = true;
return function() {
if (!canRun) {
return false;
}
canRun = false;
setTimeout(() => {
fn();
canRun = true;
}, 1000);
}
}
const move = () => {
console.log('11')
}
div.addEventListener('mousemove', throttle(move));
</script>
</html>