Open
Description
一、防抖节流异同比较
相同点:
- 都可以通过使用
setTimeout
实现。 - 目的都是,降低回调执行频率。节省计算资源。
不同点:
- 函数防抖:在一段连续操作结束后,处理回调,利用
clearTimeout
和setTimeout
实现。函数节流:在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能。 - 函数防抖关注一定时间连续触发的事件只在最后执行一次,而函数节流侧重于一段时间内只执行一次(如技能冷却)。
二、防抖函数实现
常用版
/**
* delay 延迟多久后执行
*/
function debounce(fn, delay) {
var timer; // 维护一个 timer
return function () {
var _this = this; // 取debounce执行作用域的this
var args = [...arguments];
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
fn.apply(_this, args); // 用apply指向调用debounce的对象,相当于_this.fn(args);
}, delay);
};
}
测试用例:
// test
function testDebounce(e, content) {
console.log(e, content);
}
var testDebounceFn = debounce(testDebounce, 1000); // 防抖函数
document.onmousemove = function (e) {
testDebounceFn(e, 'debounce'); // 给防抖函数传参
}
高配版
/**
* fn 传入的要防抖的函数
* wait 多久执行的时间阈值
* immediate 为 true 时,表示函数在每个等待时延的开始被调用。immediate 为 false 时,表示函数在每个等待时延的结束被调用。
*/
function debounce(func, wait, immediate = true) {
let timeout;
// 延迟执行函数
// 因为setTimeout内this为全局,context参数是为了改变setTimeout内部this指向以便需要的时候使用
const later = (context, args) => setTimeout(() => {
timeout = null; // 倒计时结束
if (!immediate) {
//执行回调
func.apply(context, args);
// 将闭包内变量赋值为null,用于垃圾回收
context = args = null;
}
}, wait);
let debounced = function () {
let args = arguments;
if (!timeout) {
timeout = later(this, args);
if (immediate) { // 第一次点击马上执行
func.apply(this, args);
}
} else {
clearTimeout(timeout);
//函数在每个等待时延的结束被调用
timeout = later(this, args);
}
}
//提供在外部清空定时器的方法
debounced.cancel = function () {
clearTimeout(timeout);
timeout= null;
};
return debounced;
};
使用
用debounce来包装scroll的回调
const better_scroll = debounce((log) => console.log(log), 1000);
document.addEventListener('scroll', better_scroll("滚动事件防抖"))
防抖的应用场景
- 搜索框输入查询,如果用户一直在输入中,没有必要不停地调用去请求服务端接口,等用户停止输入的时候,再调用,设置一个合适的时间间隔,有效减轻服务端压力。
- 表单验证
- 按钮提交事件。
- 浏览器窗口缩放,resize事件(如窗口停止改变大小之后重新计算布局)等。
三、节流函数实现
常用版
function throttle(fn, delay) {
var previous = 0;
// 使用闭包返回一个函数并且用到闭包函数外面的变量previous
return function() {
var now = new Date();
if(now - previous > delay) {
fn.apply(this, arguments);
previous = now;
}
}
}
测试用例:
// test
function testThrottle(e, content) {
console.log(e, content);
}
var testThrottleFn = throttle(testThrottle, 1000); // 节流函数
document.onmousemove = function (e) {
testThrottleFn(e, 'throttle'); // 给节流函数传参
}
高配版
/**
* fn 传入的要节流的函数
* wait 多久执行的时间阈值
*/
function throttle(fn, wait) {
let timer, pre = 0;
const throttle = function () {
const context = this; // 保留调用时的this上下文
const args = arguments; // 保留调用时传入的参数
let now = +new Date();
if (now - pre < wait) {
clearTimeout(timer);
timer = setTimeout(() => {
pre = now;
fn.apply(context, args);
}, wait);
} else {
pre = now;
fn.apply(context, args);
}
}
throttle.cancel = function () {
clearTimeout(timer);
timer = null;
}
return throttle;
}
const fn = function (log) {
console.log(log);
}
const better_scroll = throttle(fn, 1000);
document.addEventListener("scroll", better_scroll("添加节流"));
节流的应用场景
- 按钮点击事件
- 拖拽事件
- onScoll
- 计算鼠标移动的距离(mousemove)