一、概念
防抖(debounce) :
防止抖动,当你持续触发事件时,一定时间段内没有再次触发事件,事件处理函数才会执行一次,以此往后顺延说人话就是:你就一直抖吧!我就静静地看着你抖,等你不抖了我再执行
节流(throttle) :
节约流量,当你持续处罚事件时,保证一定时间段内只调用一次事件处理函数,固定时间段固定时间间隔处理函数说人话就是:你就一直在那触发吧!我有我自己的节奏,我每隔一定时间段执行一次
二、分别适用什么场景
防抖:input (一直输入,只要最后一次不再输入才会执行)节流:resize、scroll (一定要执行的,但避免执行频率过高)
三、退后,我要开始手写了
面试官问 1:怎么写一个防抖函数
function debounce(fn, delay) {
let timer = 0
return function() {
// 如果这个函数已经被触发了,就清除掉重新生成
if(timer){
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, arguments); // 透传 this 和参数
timer = 0
}, delay)
}
}
搞定
面试官问 2:怎么手写一个节流函数
1. 时间戳写法
function throttle(fn, delay) {
let last = 0; // 上次执行的时间
return function(){
let now = Date.now(); // 当前时间
if(now - last >= delay) {
last = now;
fn.apply(this, arguments)
}
}
}
function handle(){
console.log('被执行了!')
}
const throttleHandle = throttle(handle, 1000);
throttleHandle();
throttleHandle();
throttleHandle();
throttleHandle();
心想短短十几行代码搞定,稳了稳了
这时面试官问道:你这个写法第一次触发会立即执行吗,最后一次触发会执行吗?
我一看,果然第一次是立即执行,最后一次也不一定会执行
面试官接着问:怎么写一个不立即执行的节流函数?
被逼无奈,重新写
2. 定时器写法
function throttle(fn, delay){
let timer = null;
return function(){
let context = this;
let args = arguments;
if(!timer){
timer = setTime(function(){
fn.apply(context, args);
timer = null;
}, delay)
}
}
}
当你长舒一口气,以为终于要过了的时候
这时候面试官又问了:这种写法是不是最后一次触发后也要 delay 延迟啊?万一用户点击最后一次后立即退出了页面怎么办?
面试官说的有理有据啊,没办法,继续重写
3. 时间戳定时器写法结合起来
function throttle(fn, delay){
let timer = null;
let startTime = Date.now();
return function(){
let curTime = Date.now(); // 当前时间,用来计算剩余时间(从上次执行完到现在还有多久执行下次函数)
let remainning = delay - (curTime - startTime);// curTime - startTime 本次间隔已经过去多久
let context = this;
let args = arguments;
clearTimeout(timer);
if(remainning <= 0){ // 说明应该立即执行下一次事件
fn.apply(context, args);
startTime = Date.now();
} else { // 距离下次还剩余时间,重新生成 timer,延迟时间改为 remainning
timer = setTimeout(fn, remainning);
}
}
}
这回妥了,喜滋滋
4. 节流函数总结
- 时间戳写法:缺点是第一次会立即执行,最后一次不一定会执行
- 定时器写法:缺点是最后一次会延迟执行
- 时间戳 + 定时器写法:完美解决