<!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 src="./节流函数.js"></script>
</body>
</html>
// 节流函数.js
var count = 1;
var container = document.getElementById('container');
function getUserAction(e) {
console.log(this) // this 应该指向div
console.log(e) // this 不应该打印undefined
container.innerHTML = count++;
};
// container.onmousemove = throttle1(getUserAction, 500);
// container.onmousemove = throttle2(getUserAction, 500);
container.onmousemove = throttle3(getUserAction, 500);
防抖和节流的作用都是防止函数多次调用。
区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于wait,防抖的情况下只会调用一次,而节流的 情况会每隔一定时间(参数wait)调用函数。
- 防抖 触发高频事件后 n 秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间
- 节流 高频事件触发,但在 n
秒内只会执行一次,本来10秒钟执行100次,变为10秒钟执行10次,所以节流会稀释函数的执行频率,每次触发事件时都判断当前是否有等待执行的延时函数
防抖动和节流本质是不一样的。防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。
第一版 使用时间戳
第一种方法:使用时间戳
- 当触发事件的时候,我们取出当前的时间戳,然后减去之前的时间戳(最一开始值设为 0 )
- 如果大于设置的时间周期,就执行函数,然后更新时间戳为当前的时间戳
- 如果小于,就不执行。
function throttle1(func, wait) {
var context, args;
var previous = 0;
return function () {
// 设置当前时间戳
var now = +new Date();
// 解决this 指向问题
context = this;
// 解决event 对象打印undefined 问题
args = arguments;
if (now - previous > wait) {
// apply 函数改变this 指向div
func.apply(context, args);
previous = now;
}
}
}
第二版 使用定时器
第二种实现方式,使用定时器。
- 当触发事件的时候,我们设置一个定时器,再触发事件的时候
- 如果定时器存在,就不执行
- 直到定时器执行,然后执行函数,清空定时器,这样就可以设置下个定时器。
function throttle2(func, wait) {
var timeout;
return function () {
// 解决this 指向问题
context = this;
// 解决event 对象打印undefined 问题
args = arguments;
if (!timeout) {
// timeout 定时器标识。等待wait 时间后清空定时器,执行函数
timeout = setTimeout(function () {
// 清空定时器
timeout = null;
// apply 函数改变this 指向div
func.apply(context, args)
}, wait)
}
}
}
比较两个方法:
- 第一种事件会立刻执行,第二种事件会在 n 秒后第一次执行
- 第一种事件停止触发后没有办法再执行事件,第二种事件停止触发后依然会再执行一次事件 第
三版 双剑合璧
那我们想要一个什么样的呢?
有人就说了:我想要一个有头有尾的!就是鼠标移入能立刻执行,停止触发的时候还能再执行一次!
function throttle3(func, wait) {
var timeout, context, args, result;
var previous = 0;
var later = function() {
previous = +new Date();
timeout = null;
func.apply(context, args)
};
var throttled = function() {
var now = +new Date();
//下次触发 func 剩余的时间
var remaining = wait - (now - previous);
context = this;
args = arguments;
// 如果没有剩余的时间了或者你改了系统时间
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
};
return throttled;
}