防抖
- 什么是防抖? 为什么要防抖?
频繁触发 , 只在停下来 n 秒之内无触发后 , 才执行相关操作 ; 因为有些复杂的操作, 如操作DOM , 频繁的触发很影响性能, 可能会把浏览器卡住.
核心
- 维护一个定时器, 也就是说对用户的操作 , 做延迟执行
- 每次触发都先清除 1 中的定时器, 不管执行了与否 .
简单实现
function debounce(func, waitTime) {
let timeoutTimer = null;
return function (...args) {
clearTimeout(timeoutTimer)
timeoutTimer = setTimeout(() => func.apply(this, args), waitTime);
}
}
// 测试
document.querySelector('#click-test').onclick = debounce(function (...args) {
console.log(this, args)
}, 2000);
debounce 函数执行返回一个包装好的函数 , 然后绑定到 click 事件上. 参数 , this 和未包装前保持一致可用.
- 应用场景
- markdown 的实时预览 , 等到停下来输入的时候 , 才会在预览窗口更新内容
节流
- 什么是节流? 为什么要节流?
频繁触发 , 有个时间轴控制每 n 秒执行一次.
核心
- 打点记录法, 首次触发, 立即执行 , 打点为首次触发执行时间点为 o , 然后 n 秒内, 若有触发 , 则 o+n 秒后触发执行一次, 记住新的函数执行触发点, 依此循环; 若在触发执行后的 n 秒内都没有操作 , 则下次触发直接作为原点 o 执行. 回到最开始.
- . 第一次触发 , 可以选择直接触发, 最后一次触发, 后续无操作, 也应该可以得到触发.
实现
function throttle(func, interval) {
let actionPoint = 0; // 函数执行的时间点
let timeoutTimer = null;
return function (...args) {
let now = Date.now()
// 如果当前触发点的时间 与 上次执行时的时间大于 一个 interval , 则说明连续触发已经中断了, 立马执行
if (now - actionPoint > interval) {
func.apply(this, args);
actionPoint = now; // 记录第一次执行点
}
else {
// 如果还在上次执行之后的 interval 之内, 且未开启定时时 ,以上次执行点为基准算出还有多久执行;
if (!timeoutTimer) {
var leaveTime = interval - (now - actionPoint);
actionPoint += interval; // 记录下一次执行点
timeoutTimer = setTimeout(() => {
func.apply(this, args);
clearTimeout(timeoutTimer);
timeoutTimer = null;
}, leaveTime);
}
}
}
}
// 测试
document.querySelector('#click-test').onclick = throttle(function (...args) {
console.log(this)
console.log(args)
console.log(new Date().getSeconds())
}, 5000)
基本思路: 首次或者已经间断很久没执行的 , 那么其上一次执行点 , 必然是0 或者相对现在的时间点的距离是大于
interval
的 , 立马让它执行一遍 ; 如果是在距离上次触发点小于interval
, 如果此时有定时器, 就忽略, 没有就开启一个定时器, 延迟执行.
防抖和节流的异同
相似
- 都是针对事件频繁触发的优化
差异
- 防抖是事件连续触发 , 但仅在事件停止后 n 秒响应一次; 节流是事件持续触发, 但固定间隔 n 秒响应一次.