防抖就是对高频率,连续触发的代码进行优化处理。使其在多次执行过程中,只执行一次。主要利用js闭包来实现。
防抖的实现有三步:
- 防抖方法封装
- 传参决定是否立即执行
- this指向
基础防抖方法如下
function debounce(fn, timer) {
let time = null; //闭包保存定时器
return function (e) {
if (time) {
clearTimeout(time)
}
time = setTimeout(() => {
fn()
}, timer)
}
}
debounce方法有两个参数
- fn:需要防抖的方法
- timer:连续触发的时间间隔
首先定义闭包来保存time参数,然后返回一个方法,返回方法里面的逻辑也比较简单,如果time有值那就清空这个time,然后设置time为定时器,它将在tmer间隔后执行方法fn。因为time是闭包,所以time会被缓存下来,带在返回方法上,因此在下一次触发后,先判断上次事件中time是否为空,为空则清除后再设置time,以此循环。
传参决定是否立即执行
原生防抖函数可以带个bollean值来判断是否立即执行,继续优化代码
function debounce(fn, isdo, timer) {
let time = null; //闭包保存定时器
return function () {
let that = this
if (time) {
clearTimeout(time)
}
if (isdo) {
if (!time) {
fn()
}
time = setTimeout(() => {
time = null
}, timer)
} else {
time = setTimeout(() => {
fn()
}, timer)
}
}
}
相比基础版,新加了isdo参数,思路其实是一样的,isdo为false,则和基础版一样,isdo为true,则立即执行,首先判断!time是否不为空,也就是time是否为null,time为null,则直接执行fn,然后将time设置为定时器,在timer后设置time为null,如果规定间隔连续触发,则time不为null,将会重新设置time,使得定时器重新开始,直到两次触发超过时间间隔。
- this指向
在fn方法中打印this,指向window,因此在调用时需要通过apply来让this绑定到调用debounce的对象上,绑定后打印为sub的dom对象。 下面是完整代码。
const sub = document.getElementsByTagName('input')[0];
let con = document.getElementById('ind')
let sum = 0
function add() {
console.log(this,'dadaw'); //<input type="submit">
con.innerText = sum++
}
sub.addEventListener('click', debounce(add, true, 1000))
function debounce(fn, isdo, timer) {
let time = null; //闭包保存定时器
return function (e) {
console.log(this); // <input type="submit">
let that = this
if (time) {
clearTimeout(time)
}
if (isdo) {
if (!time) {
fn.apply(that,arguments)
}
time = setTimeout(() => {
time = null
}, timer)
} else {
time = setTimeout(() => {
fn.apply(that,arguments)
time = null
}, timer)
}
}
}
补一个手写节流的方法,思路一样的,就是在同一时间段只执行一次,执行后更新oldTime
var cont = 1
let but = document.getElementsByTagName('input')[0]
let conent = document.getElementById('ind')
let someThing = function (e) {
conent.innerText = cont++
}
but.addEventListener('click', expenditure(someThing, 2000))
function expenditure(fn, timer) {
let oldTime = 0;
return function () {
nowTime = new Date().getTime()
if (nowTime - oldTime > timer) {
fn()
oldTime = nowTime
}
}
}