3. 工作中用过防抖和节流吗?
3.1 基本概念:
在进行窗口的resize、scroll,输入框内容校验等操作时,如果事件处理函数调用的频率无限制,会加重浏览器的负担,导致用户体验非常糟糕。此时我们可以采用debounce(防抖)和throttle(节流)的方式来减少调用频率,同时又不影响实际效果。
- 防抖:当持续触发一个事件时,一定时间内没有再触发事件时,事件处理函数才会执行,一直向后顺延;
- 节流:当持续触发一个事件时,保证一定时间段内只调用一次事件处理函数;
3.2 分别适合用在什么场景:
- 节流:input、button、scroll
- 防抖:input、resize、button
3.3 手写一个防抖函数:
function debounce(fn, delay) {
let timer = null;
return function () {
let context = this;
let args = arguments;
if (timer !== null) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(context, args);
}, delay);
}
}
3.4 手写一个节流函数:
// 使用时间戳
// 当高频事件触发时,第一次会立即执行,而后再怎么频繁地触发事件,也都是每interval时间才执行一次。
// 而当最后一次事件触发完毕后,事件可能不会再被执行了
function throttle(fn, interval) {
let last = Date.now();
return function () {
let now = Date.now();
if (now - last >= interval) {
last = now;
fn.apply(this, arguments);
}
}
}
// 使用定时器
// 当第一次触发事件时,不会立即执行函数,而是在interval秒后才执行。而后再怎么频繁触发事件,也都是每interval时间才执行一次。当最后一次停止触发后,由于定时器的delay延迟,可能还会执行一次函数。
function throttle(fn, interval) {
let timer = null;
return function () {
let context = this;
let args = arguments;
if (timer === null) {
timer = setTimeout(() => {
fn.apply(context, args);
timer = null;
}, interval);
}
}
}
// 结合上述两者,当第一次触发事件时马上执行事件处理函数,最后一次触发事件后也还会执行一次事件处理函数。
function throttle(fn, interval) {
let timer = null;
let startTime = Date.now();
return function () {
let currentTime = Date.now();
let remainTime = interval - (currentTime - startTime);
let context = this;
let args = arguments;
if (remainTime <= 0) {
fn.apply(context, args);
startTime = Date.now();
} else {
timer = setTimeout(fn, remainTime);
}
}
}
// 感觉更好的方法:
function throttle (fn, interval) {
let timer = null;
return function () {
if (timer === null) {
fn.apply(this, arguments);
timer = setTimeout(() => {
timer = null;
}, interval);
}
}
}