防抖函数中容易出现的问题
我们生活中在用手机电脑时,有时网速不好,然后你心急,就一直点某个按钮,一直发送请求,如果服务器一直接收你的所有的请求的话,这么多人迟早会爆掉。
这时,防抖函数的作用就体现出来了,他主要是是防止过多相同的请求发送到服务器,他一般会设置一个时间段,你点第一次的时候,开始计时,如果你在这个时间段里发送了第二次请求,那么它就会清除第一次请求,保留第二次请求,又重新计时,以此类推,一直到你没有在这个时间段里发送请求了,自然就不会清除你当前的请求了,所以小伙伴们,以后知道该以什么样的速度点了吧,最好点击时间间隔1秒以上。
防抖函数
没实现防抖功能的函数:
let btn = document.getElementById('btn');
btn.addEventListener('click', () => {
setTimeout(() => {
console.log('提交');
}, 1000)
})
连续快速点击后
实现防抖功能的函数:
// 获取按钮元素
let btn = document.getElementById('btn');
// 定义处理点击事件的函数
function handle() {
console.log('提交');
}
// 为按钮添加点击事件监听器,调用防抖函数包装的处理函数
btn.addEventListener('click', debounce(handle));
// 防抖函数
function debounce(fn) {
let timer = null; // 定时器变量,用于存储定时器ID
return function (e) {
clearTimeout(timer); // 清除之前的定时器,确保只有最后一个定时器生效
timer = setTimeout(fn, 1000); // 设置新的定时器,延迟执行传入的处理函数 fn
}
}
连续快速点击后
防抖函数中的问题
实现防抖函数不难,但是却存在两个问题,this指向和事件(e)被篡改,有些人可能对这两个问题不太懂,看如下:
1. this指向出错
输出handle中的this:
let btn = document.getElementById('btn');
function handle() {
//ajax请求
console.log('提交',this);
}
btn.addEventListener('click', debounce(handle))
function debounce(fn) {
let timer = null;
return function () {
clearTimeout(timer)
timer = setTimeout(fn, 1000)
}
}
this指向window,这显然不是我们想要的,你想一下,我要handle里this指向window有什么用,又不要用到window里的属性,我现在要用的是btn里的属性,况且,原本this指向的就是btn,只不过由于要实现防抖功能把handle当成了参数传递给了debounce函数,因此handle里this指向发生了转变,为了this指向重回正轨,我们有两种方法:
- 使用箭头函数和call
let btn = document.getElementById('btn');
function handle() {
console.log('提交', this);
}
btn.addEventListener('click', debounce(handle))
function debounce(fn) {
let timer = null;
return function () {
clearTimeout(timer)
timer = setTimeout(() => {
fn.call(this)
}, 1000)
}
}
改变this指向,可以使用显示绑定,强行掰弯this指向。我们分析了下,发现debounce函数里的this指向的是btn,于是我们这样做:箭头函数没有this,所以箭头函数里的this,是外层函数debounce的,用call将fn中this掰到了debounce函数里this,this就回到正轨了。
诶~,舒服了。
- 保存this,和使用call
let btn = document.getElementById('btn');
function handle() {
console.log('提交', this);
}
btn.addEventListener('click', debounce(handle))
function debounce(fn) {
let timer = null;
return function () {
const that = this
clearTimeout(timer)
timer = setTimeout(function () {
fn.call(that)
}, 1000)
}
}
2. 事件(e)改变
handle函数里,输出事件e
let btn = document.getElementById('btn');
function handle(e) {
console.log('提交', e);
}
btn.addEventListener('click', debounce(handle))
function debounce(fn) {
let timer = null;
return function () {
const that = this
clearTimeout(timer)
timer = setTimeout(function () {
fn.call(that)
}, 1000)
}
}
handle函数中的事件e又跑哪去了。。。。。
看看debounce函数中的有没事件e(叹气):
let btn = document.getElementById('btn');
function handle(e) {
console.log('提交', e);
}
btn.addEventListener('click', debounce(handle))
function debounce(fn) {
let timer = null;
return function (e) {
console.log(e,'-----');
const that = this
clearTimeout(timer)
timer = setTimeout(function () {
fn.call(that)
}, 1000)
}
}
好家伙,原来是跑这里来了,debounce
函数简直就是强盗,作为一名新世纪三好青年,必须得纠正这种行为,直接用call把事件给抢回来(义愤填膺),如下:
let btn = document.getElementById('btn');
function handle(e) {
console.log('提交', e);
}
btn.addEventListener('click', debounce(handle))
function debounce(fn) {
let timer = null;
return function (e) {
console.log(e,'-----');
const that = this
clearTimeout(timer)
timer = setTimeout(function () {
fn.call(that, e)
}, 1000)
}
}
做完这些后,有强迫症的我终于放松了。
结语
有些人可能看这篇文章有点困难,因为看懂这篇需要对this指向和闭包有较深的理解,当然还有事件(e),这些人可以先去弄懂闭包和this,再来反复观看此文,如果不知道看哪篇文章,可以看我之前写的关于闭包和this指向的文章,当然我可能写的也不是很好,甚至出错。我写文章,只是想巩固自己的知识点以及加深对this和闭包的理解,如果对你有帮助的话,那就再好不过了,感谢阅读!祝你天天向上!!!
转载自:https://juejin.cn/post/7367659706866810891