🧑💻秒杀!前端常见手写题!-HowieCong
一、防抖
-
防抖:是一种优化高频率触发事件的技术,当一个事件在短时间内被频繁触发时,防抖函数会等待一段时间,如果在这段时间内没有再次触发该事件,才会执行相应的操作;如果在等待时间内再次触发了事件,则会重新计时
-
实现思路:主要是借助定时器,在每次事件触发时清除之前的定时器,并重新设置一个新的定时器
二、编码实现
(1)基础的防抖(非立即执行)
// 定义一个防抖函数,接受两个参数,func是需要防抖处理的函数,wait是延迟的时间
function debounce(func,wait){
// 用于存储定时器的变量
let timer = null;
// 返回一个新的函数,该函数会在调用时进行防抖处理
return function(){
// 保存函数调用时的上下文
const context = this;
// 保存函数调用时的参数
const args = arguments;
// 如果定时器存在,清除它
if(timer){
clearTimeout(timer);
}
// 重新设置一个新的定时器,在指定的 wait 时间后执行 func 函数
timeout = setTimeout(() => {
func.apply(context,args)
},wait);
};
}
// 测试示例
function exampleFunctions(){
console.log('Function executed');
}
// 创建一个防抖后的函数,等待时间为500毫秒
const debouncedFunction = debounce(exampleFunctions,500);
// 模型频繁处触发事件
debouncedFunction();
debouncedFunction();
debouncedFunction();
(2)立即执行版防抖函数
// 定义一个防抖函数,接受两个参数,func是需要防抖处理的函数,wait是延迟的时间,immediate表示是否立即执行
function debounce(func,wait,immediate = false){
// 用于存储定时器的变量
let timer = null;
// 返回一个新的函数,该函数会在调用时进行防抖处理
return function(){
// 保存函数调用时的上下文
const context = this;
// 保存函数调用时的参数
const args = arguments;
// 清除之前的定时器,确保不会重复执行
if(timer){
clearTimeout(timer);
}
if(immediate){
// 如果是立即执行模式
// 判断是否已经过了等待时间(即定时器为空)
const callNow = !timer;
// 设置新的定时器,在等待时间后将定时器设置为null
timer = setTimeout(() => {
timer = null;
},wait);
// 如果已经过了等待时间,立即执行函数
if(callNow){
func.apply(context,args);
}
}else{
// 如果不是立即执行模式,设置定时器在等待时间后执行函数
timer = setTimeout(() => {
func.apply(constext,args);
},wait);
}
};
}
// 测试示例
function exampleFunction() {
console.log('Function executed');
}
// 创建一个立即执行的防抖后的函数,等待时间为 500 毫秒
const debouncedFunction = debounce(exampleFunction, 500, true);
// 模拟频繁触发事件
debouncedFunction();
debouncedFunction();
debouncedFunction();
(3)能取消当前等待版防抖函数
// 定义一个防抖函数,接受两个参数,func是需要防抖处理的函数,wait是延迟的时间,immediate表示是否立即执行
function debounce(func,wait,immediate = false){
// 用于存储定时器的变量
let timer = null;
// 返回一个新的函数,该函数会在调用时进行防抖处理
return function(){
// 保存函数调用时的上下文
const context = this;
// 保存函数调用时的参数
const args = arguments;
// 清除之前的定时器,确保不会重复执行
if(timer){
clearTimeout(timer);
}
if(immediate){
// 如果是立即执行模式
// 判断是否已经过了等待时间(即定时器为空)
const callNow = !timer;
// 设置新的定时器,在等待时间后将定时器设置为null
timer = setTimeout(() => {
timer = null;
},wait);
// 如果已经过了等待时间,立即执行函数
if(callNow){
func.apply(context,args);
}
}else{
// 如果不是立即执行模式,设置定时器在等待时间后执行函数
timer = setTimeout(() => {
func.apply(constext,args);
},wait);
}
};
// 添加取消方法
debounced.cancel = function(){
clearTimeout(timer);
timer = null;
}
return debounced;
}
// 测试示例
function exampleFunction() {
console.log('Function executed');
}
// 创建一个立即执行的防抖后的函数,等待时间为 500 毫秒
const debouncedFunction = debounce(exampleFunction, 500, true);
// 模拟频繁触发事件
debouncedFunction();
debouncedFunction.cancel();
三、讨论
(1)防抖函数中的timer变量为什么要放在闭包里面?
-
timer变量放在闭包中是为了在多次调用返回的防抖函数时,能够保留定时器的状态信息 -
如果
timer变量不放在闭包中,每次调用返回的函数时,timer都会被重新初始化为null,这样就无法实现清除之前定时器的功能,也就无法达到防抖的效果
(2)立即执行版和非立即执行版防抖函数有什么应用场景上的区别?
-
非立即执行版防抖函数适用于一些不需要立即响应的场景,比如搜索框的输入提示。用户在输入过程中可能会频繁输入字符,使用非立即执行版防抖函数可以在用户停止输入一段时间后再进行搜索提示的请求,减少不必要的请求次数
-
立即执行版防抖函数适用于需要立即响应,并且在一段时间内不希望重复触发的场景,比如按钮的点击事件。用户点击按钮后立即执行相应操作,并且在一段时间内再次点击按钮不会触发新的操作,避免用户误操作
(3)防抖函数的时间复杂度和空间复杂度分别是多少?
-
时间复杂度:每次调用防抖函数时,主要操作是清除定时器和设置新的定时器,这些操作的时间复杂度都是O(1),因此防抖函数的时间复杂度是 O(1)
-
空间复杂度:主要的空间开销是存储定时器的变量
timer,无论输入的函数和等待时间如何,只需要一个额外的变量来存储定时器,因此空间复杂度也是(O(1))
(4)防抖和节流的区别是什么
-
防抖是在事件触发后等待一段时间再执行,如果在这段时间内事件再次被触发,则重新计时
-
而节流是保证函数在指定时间间隔内只执行一次,无论事件被触发多少次
(5)防抖函数如何支持立即执行
-
可以通过添加一个立即执行的标志位
immediat**如何选择防抖的延迟时间**e来实现 -
当
immediate为true时,函数会在第一次触发事件时立即执行,之后的行为按照正常的防抖逻辑处理
(6)防抖函数如何取消
-
可以通过定义一个取消函数来清除定时器,从而取消防抖函数的执行
-
例如,在
debounce函数中添加一个cancel方法,调用clearTimeout(timeout)来清除定时器
(7)如何选择防抖的延迟时间
- 防抖的延迟时间需要根据具体的场景和用户体验来选择。一般来说,延迟时间不宜过长,以免影响用户体验
❓其他
1. 疑问与作者HowieCong声明
-
如有疑问、出错的知识,请及时点击下方链接添加作者HowieCong的其中一种联系方式或发送邮件到下方邮箱告知作者HowieCong
-
若想让作者更新哪些方面的技术文章或补充更多知识在这篇文章,请及时点击下方链接添加里面其中一种联系方式或发送邮件到下方邮箱告知作者HowieCong
-
声明:作者HowieCong目前只是一个前端开发小菜鸟,写文章的初衷只是全面提高自身能力和见识;如果对此篇文章喜欢或能帮助到你,麻烦给作者HowieCong点个关注/给这篇文章点个赞/收藏这篇文章/在评论区留下你的想法吧,欢迎大家来交流!