前言:面临秋招,防抖节流是重要的考点,是前端用来优化高频事件的方法,写个文章梳理一下。
-
防抖:规定时间内只执行一次。 (重新计算时间)
-
逻辑:当事件被触发时,不会立即执行对应的处理函数,而是等待一段预设的时间。
-
场景:掘金登录的时候,用户🦊一直狂摁登录按键,会导致多次登录请求,消耗性能。 因此采用防抖的方式,每次摁登录按键,清除之前的等待时间,再等一段时间,这样让狐狸先生最后一次的点击登录按键成功。
-
项目中使用,引入lodash库:
js
import _ from "lodash"
<button onclick={_.debounce(()=>navigate("./home"),500)}/>
//debounce()传入两个参数一个是函数体一个是number
注意到debounce中需要传入两个参数 ,一个是函数体 fn,一个是时间delay,表示需要等待delay时间后执行fn,定下大体逻辑。
js
const debounce = (fn,delay)=>{
//直接采用定时器 , delay时间后调用fn
setTimeout(() => {
fn()
}, delay)
}
显然是有问题的,因为onclick需要接收一个函数,再调用它,改一下返回一个函数体,注意这里是函数体,如果是匿名函数则this在 V8 非严格模式 会指向window,node环境指向undefined
js
const debounce = (fn, delay) => {
return function () {
setTimeout(() => {
fn()
}, delay)
}
}
还有问题,这里的定时器只会执行一次,明显不满足重新计算时间的要求,稍微改一下,创建一个timer,返回一个函数体,因为返回函数体 中用上了timer ,会形成一个闭包
js
const debounce = (fn, delay) => {
let timer = null;
return function () {
//这里有个闭包 timer
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn()
}, delay)
}
}
如果这个放到button里面会是什么样呢?
js
//<button onclick={debounce(()=>navigate("./home"),500)}/>
<button onclick={
function () {
//这里有个闭包 timer 每一次点击时的timer都是同一个哦
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
navigate("./home")
}, 500)
}
}/>
现在每次点击button,都会把上次的定时器清除,看起来大功告成。(你看看你后面,你再看看你后面,你再看看你后面......)
注意平常我们调用onclick中的会传入一个e(事件参数)以及其它参数,这里没法传事件参数,再改一点点代码。
js中的 "... " 有两种语义,一种是收集,一种是解构,所以我用"... "来收集 传入的参数再用"... "将传入参数解构出来
js
const debounce = (fn, delay) => {
let timer = null;
return function (...args) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn(...args)
}, delay)
}
}
这样就完成了吗,这里的fn 函数是独立调用的,如果fn中有用this,this在 V8 非严格模式下会指向window,node环境指向undefined,这明显有bug,稍微再改一点,给 this 掰弯来。
js
const debounce = (fn, delay) => {
let timer = null;
return function (...args) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, args)//apply接受的是数组
//或者
fn.call(this,...args)//call能接受独立的值
}, delay)
}
}
检查一遍,写回button里看看
js
// <button onclick={debounce(
// (e)=>{
// console.log(e)
// }
//,500)}/>
<button onclick={
function (e) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout((e) => {
// fn.apply(this, [e])//apply接受的是数组
console.log(e)
}, 500)
}}/>
好像没问题欸嘿,面试一定写得出来。
结果
js
const debounce = (fn, delay) => {
let timer = null;
return function (...args) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}