在前端的开发中有一部分用户的行为会频繁的触发事件的执行,如scroll,mousemove等等,函数被非常频繁的调用,从而造成相当大的性能问题。这才有了函数的节流与防抖。
节流:函数的节流是预定一个函数只有在大于等于执行周期时才会执行,周期内调用不会执行(对于持续触发的事件,规定一个间隔时间,每隔一段时间只能执行一次)。
场景:window.onresize(scroll)事件。对window对象绑定resize事件,当浏览器窗口大小被拖动改变时,这个事件的触发频率很高,这个时候浏览器可能吃不消从而造成卡顿。
mousedown事件。单位时间快速的点击(限时抢购)
我们可以用两中方法来实现节流
1、时间戳 :只要触发,就要用到Date获取到现在的时间,与上一次的时间进行比较。如果时间差大于了规定的等待时间,就可以执行一次(强调:函数执行后要更新上一次时间的值),否则就等下一次触发时继续进行比较。
2、定时器:用定时器来实现时间的间隔,当定时器不存在时定义一个定时器,执行函数后把定时器清空。如果定时器存在说明已经在等待的过程中等待下一次触发事件时再查看。
当第一次触发事件时不会立即执行函数而是要在wait秒后才执行。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<input type="text" id="input">
<script>
//函数节流
//1.时间戳
function throttle(fn, wait) {
let preTime = 0;
return () => {
const now = new Date();//时间戳
if (now - preTime >= wait) {
fn();
preTime = now; //执行更新后的值
}
}
}
let input = document.querySelector('input');
function look() {
console.log(input.value);
}
input.onclick = throttle(look, 100);
// //2.定时器
// function throttle(fn, wait) {
// let timer;
// return () => {
// if (!timer) {
// timer = setTimeout(() => {
// timer = null;
// fn();
// }, wait);
// }
// }
// }
</script>
</body>
</html>
例子:限时抢购
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>节流</title>
</head>
<body>
<button id='show'>抢购</button>
<div id="box">0</div>
<script>
let btn = document.getElementById('show');
let box = document.getElementById('box');
//wait间隔的时间,fn代表buy函数
function throttle(fn, wait) {
let lastTime = 0;
return function (e) {
let nowTime = new Date().getTime()
if (nowTime - lastTime > wait) {
fn();
lastTime = nowTime;
}
}
}
function buy() {
box.innerText = parseInt(box.innerText) + 1
}
btn.onclick = throttle(buy, 1000)
</script>
</body>
</html>
防抖:防止函数抖动,在执行触发事件的情况下,元素的位置或尺寸属性快速的发生变化造成页面回流,出现元素抖动的现象,通过函数防抖可以使得元素的位置或者尺寸属性延迟变化,减少页面回流。简单来说,任务频繁触发的情况下,只有任务触发的间隔超过指定间隔的时候任务才会执行。防止函数过于频繁的不必要的调用(对于在事件被触发n秒后再执行的回调,如果在这n秒内又被触发,那就重新开始计时)。
场景:文本输入keydown 事件,mousemove ,表单组件输入内容验证.......
实现:要用到定时器,setTimeout实现计时和clearTimeout实现重新开始计时。只要触发就会清除上一个计时器,紧接着又生成一个新的计时器,直到停止触触发wait时间后,才会执行回调函数。不断的触发事件就会不断的重复这个过程,达到防止目标函数过于频繁调用的目的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>防抖</title>
<style>
div {
width: 400px;
height: 200px;
background: blanchedalmond;
font-size: 50px;
text-align: center;
margin: 0 auto;
}
</style>
</head>
<body>
<div>0</div>
<script>
const div = document.querySelector('div');
let n = 0;
function debounce(fn, wait) {
let timer;
return () => {
let context = this; //传给目标函数
//清理掉正在执行的函数,并且重新执行
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(context, arguments); //修复
}, wait);
}
}
function look() {
n++;
div.innerHTML = n;
}
div.onmousemove = debounce(look, 100);
</script>
</body>
</html>
节流与防抖的比较:二者都可以防止函数过于频繁的调用。一般用于性能优化。二者的区别在于:当事件持续被触发,如果触发时间间隔小于规定的等待时间那么函数防抖情况下,函数将一直推迟执行,函数节流的情况下,函数将每个n秒执行一次。一般函数节流用的较多。但还是要看具体的情况。