防抖
代码实现
假设情况 假设这里我要实现的一个场景是 用于多次点击登录按钮 那么久会多次频繁的发送网络请求给后端 我们希望在用户多次频率特别高的点击中 只会触发最后一次 这时候 就用到了节流
let btn = document.querySelector('.btn');
function debounce(fn, delay){
let timeout = null;
return function(){
clearTimeout(timeout);
timeout = setTimeout(() => {
fn()
}, delay);
}
}
function show(){
console.log(1);
}
btn.addEventListener('click', debounce(show, 2000));
在这里 实现了一个简易的节流函数 可以看到 是使用了闭包的 这里debounce函数是直接被调用的 返回了一个匿名函数 从而有了闭包的存在 当我第一次点击的时候 这时候timeout是空的 所以clearTimeout没有清除任何定时器 当我第二次点击的时候 此时已经有定时器了 那么之前的第一个定时器就会被清除 重新去设置了一个定时器 如果用户频繁的点击的话(频率在2000毫秒内) 就会每次都去清除之前的定时器 然后重新设置一个定时器 直到用户停止点击(或是在2000毫秒之后点击) 那么这个fn函数才会被触发 也就是在用户停止点击2000毫秒后这个fn函数才会被触发 我们可以在这里去发起网络请求
存在的问题
1 this指向问题
实际上在btn.addEventListener()这个监听中 第二个参数是一个函数 会在触发比如这里是click事件的时候去执行这个函数 函数的this的指向就是这个元素
btn.addEventListener('click', function(){
console.log(this);
// <button class="btn">按钮</button>
})
但是在 fn函数中this 的指向是window
function show(){
console.log(this)
// window
console.log(1);
}
解决办法 改变fn函数中this的指向问题
let btn = document.querySelector('.btn');
function debounce(fn, delay){
let timeout = null;
// 返回的这个函数的this指向就是 btn这个按钮 因为这是事件绑定的机制 可以自己去看看 后面我也会去记录下这方面的知识
return function(){
clearTimeout(timeout);
timeout = setTimeout(() => {
// 在这里 我直接绑定的this 是因为我使用的是箭头函数的方式 所以箭头函数的this就是它别定义的上下文 可以看我之间的那篇文章
fn.apply(this)
}, delay);
}
}
function show(){
console.log(1);
}
btn.addEventListener('click', debounce(show, 2000));
/*
如果不理解 也可以使用这种方式
function debounce(fn, delay){
let timeout = null;
// 返回的这个函数的this指向就是 btn这个按钮 因为这是事件绑定的机制 可以自己去看看 后面我也会去记录下这方面的知识
return function(){
const that = this;
clearTimeout(timeout);
timeout = setTimeout(function(){
// 这里 没有使用箭头函数的方式 所以这里我要讲返回的那个函数的this给记录下来 然后在这里去进行一个绑定
fn.apply(that);
}, delay)
}
}
*/
2 事件对象event的问题
实际上在btn.addEventListener()这个监听中 第二个参数是一个函数 会在触发比如这里是click事件的时候去执行这个函数 函数中会有一个默认的参数event事件对象
btn.addEventListener('click', function(e){
console.log(e)
console.log(this);
// <button class="btn">按钮</button>
})
大家可以自行的打印一下 是一个event对象 里面还包含挺多东西的 但是在我们要当点击后哟啊执行的fn函数中 是没有这个event事件对象的 打印出来的是 undefined
function show(e){
console.log(this);
// window
console.log(e);
// undefined
console.log(1);
}
解决办法 将 event 对象传入fn函数中
let btn = document.querySelector('.btn');
function debounce(fn, delay){
let timeout = null;
return function(){
// 获取这个返回函数的参数 也就是event对象
let args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(this, args);
}, delay);
}
}
function show(){
console.log(1);
}
btn.addEventListener('click', debounce(show, 2000));
代码实现-增加一个功能
在这里 我们是点击了过了delay毫秒之后再去执行这个函数 那么现在我想的是 我不仅可以实现过了delay毫秒后去执行 还可以实现的一个功能是点击后立即执行 最后一次点击不会执行 你想用哪种方式都可以 解决思路 我们可以往节流函数 debounce函数中再去传入一个参数bool值 让你自己传入true/fasle 去选择你要的一个模式
let btn = document.querySelector('.btn');
function debounce(fn, delay, bool){
let timeout = null;
return function(){
const args = arguments;
clearTimeout(timeout);
if(bool) {
if(!timeout) {
fn.apply(this, args);
}
timeout = setTimeout(() => { timeout = null}, delay);
}else {
timeout = setTimeout(() => {
fn.apply(this, args);
}, delay);
}
}
}
function show(){
console.log(1);
}
btn.addEventListener('click', debounce(show, 2000, true));
总结
两种模式的切换 一个是点击后当有频繁点击的时候 会在最后一次点击后 过了 delay毫秒之后执行 一个是点击后立即执行后面你再频繁点击后 都不会执行 直到最后一次频繁点击后 过了delay毫秒后再次点击 才会又重复这个过程 可以看代码自己好好体会下
节流
代码实现
这里就不写上面那些存在的问题了 其实都是一样的问题 上面已经解决了 这里就直接写代码的实现了
代码实现-1 时间戳的方式
function throttle(fn, delay, bool){
let previous = 0;
return function(){
let now = new Date().valueOf();
if(now - previous > delay){
fn.apply(this, arguments);
previous = now;
}
}
}
这里 当用户频繁点击的时候 只会在规定的delay时间内触发一次 过了delay时候 再触发 一直重复
代码实现2- 定时器的方式
function throttle(fn, delay, bool){
let timeout = null;
return function(){
let args = arguments;
if(!timeout){
timeout = setTimeout(() => {
timeout = null;
fn.apply(this, args);
}, delay)
}
}
}
这里 是当用户频繁点击后 会在规定的delay时间里 最后一次会触发
节流和防抖函数的区别
当用户频繁点击的时候 防抖函数是会在用户的最后一次点击后的delay时间后触发 而节流函数是会无论用户怎么频繁点击 都会在规定的delay时间内去触发一次
装载于:https://juejin.cn/post/6974670402727444494
评论(0)