最近想重新系统地整理一下前端的知识,因此写了这个专栏。我会尽量写一些业务相关的小技巧和前端知识中的重点内容,核心思想。
前言
防抖和节流是前端很基本的功能,在很多的工具库里都会有。在某些中小公司会用手写防抖,节流作为面试题。目的是考察开发者开发团队公共方法库的能力。今天我们就来尝试一下设计防抖与节流这2个功能的函数。
防抖 debounce
指在一个规定的时间范围内,只执行一次动作。换句话说,当用户规定频繁触发某个行为的时候,我们会在只执行一次该动作,其余的触发会被拦截。一般这个触发时间可以定在规定时间段的头或者尾。
手写防抖
函数参数有个3:
- func: function,必传,要执行的回调函数
- wait: number,选传,防抖时间间隔
- immediate:boolean,选传,是否立即执行(执行时刻是在时间段头还是尾)
思路:
- 先判断参数是否正确,没传的设置默认值
- 设置定时器标识timer,通过闭包返回函数(这样函数可以访问timer)
- 每次执行时,先判断当前timer是否空闲且immediate为true,说明这次需要执行函数
- 清空timer
- 重置timer
- 如果这次需要执行函数,就直接执行,并返回结果
- 如果函数在尾部执行就等定时器到期之后执行,并清空itmer。
const debounce = function debounce(func, wait, immediate) {
// 判断非法参数
if (typeof func !== "function") throw new TypeError('func 必须为可执行函数');
// 如果传入参数忽略wait,func(func,immediate)。把wait的位置内容赋给immediate
if (typeof wait === "boolean") immediate = wait;
// 如果没有wait就设置默认值
if (typeof wait !== "number") wait = 500;
// 如果没有immediate(func(func,wait)或者类型不对就设置默认值
if (typeof immediate !== "boolean") immediate = false;
// 设置时间标识
let timer = null;
return function operate(...params) {
// 立即执行的条件:1.timer空闲 2. immediate 为true
let now = !timer && immediate;
let result;
// 清空timer
timer = clearTimer(timer);
// 重置timer
timer = setTimeout(() => {
// 定时器到了之后 清空timer
timer = clearTimer(timer);
// 时间段尾部执行
if (!immediate) func.call(this, ...params);
}, wait);
// 时间段头部执行
if (now) result = func.call(this, ...params);
return result;
};
};
节流 throttle
指在固定时间段里,设置一个频率,如果用户在这个时间段里以高于我们设定的频率执行函数。我们就强制要求函数以我们规定的频率执行。
仔细想想,其实所谓的控制执行频率就是限制每2次函数执行之间的时间间隔。
手写节流
函数参数有2个:
- func: function,必传,要执行的回调函数
- wait: number,选传,函数执行的时间间隔。
思路:
- 先判断参数是否正确,没传的设置默认值
- 设置定时器标识timer,通过闭包返回函数(这样函数可以访问timer)
- 声明previous记录上次执行时间
- 如果这次需要执行函数,就直接执行,并返回结果
const throttle = function throttle(func, wait) {
// 判断非法参数
if (typeof func !== "function") throw new TypeError('func must be an function!');
// 如果没有wait就设置默认值
if (typeof wait !== "number") wait = 500;
// 设置时间标识
let timer = null
// 上次执行时间
let previous = 0;
return function operate(...params) {
// 当前时间
let now = +new Date();
// 距离上次执行的时间差
let remaining = wait - (now - previous);
let result;
if (remaining <= 0) {
// 两次间隔时间超过500ms了,让方法立即执行
// 清空定时
timer = clearTimer(timer);
// 立即执行函数
result = func.call(this, ...params);
// 记录本次执行时间
previous = +new Date();
} else if (!timer) {
// 没设置过定时器等待,则我们设置一个去等待即可
timer = setTimeout(() => {
// 清空定时
timer = clearTimer(timer);
// 执行函数
func.call(this, ...params);
// 记录本次执行时间
previous = +new Date();
}, remaining);
}
// 返回执行结果
return result;
};
};
总结
防抖和节流不是很难的知识点,但手写防抖节流除了可以考察开发者对这2个概念的掌握以外,还能看出他在编写公共工具函数时的基本功(如入参判断,定时器标识处理等)。希望对大家有所帮助。