手写防抖节流,这次还搞不懂我跟你姓
LOL和王者大家都玩过吧?我们今天就拿这个来举例
什么是防抖?
从字面意思来理解,防抖就是防止抖动,避免事件的重复触发。 LOL(王者)里按下了回城键,那么在8秒钟之后,就会执行回城事件,如果此时你再次回城,那回城时间将会重置,又需要重新过8秒才会回城。 还有就是输入框的输入,网页的滚动条,不可能是实时响应的,都是做了防抖处理的,只执行最后一次
所以,在一段时间内,将多次的高频操作做到触发最后一次 就是防抖
什么是节流?
节流就更好理解了,英雄技能就是一个典型的节流,每释放一次技能之后都需要冷却时间。这个技能是必须等cd转完之后才能再次释放, 所以说,规定的时间里只执行一次就是节流
手写防抖
// html
<input type="text">
// js
const dom = document.querySelector('input');
dom.oninput = myDebounce(function () {
// 这儿为什么不是 箭头函数 箭头函数的话 这儿就是window了 匿名函数的话this就指向fn的调用者==> dom
console.log('dom事件里面的this', this);
console.log(dom.value + '看我',);
}, 300);
function myDebounce(fn, delay) {
console.log('防抖顶层函数的 this', this); // 这儿是window
let timer = null;
return function () {
//这儿为什么不是 箭头函数 ?同理 箭头函数的话 这儿this就是window了 匿名函数的话this就指向fn的调用者==> dom
console.log('return 函数 的 this', this);
if (timer) {
clearTimeout(timer)
};
// 这儿箭头函数就是为了绑定 this 不被改变 这儿用 function this指向会改变的 (setTimeout中非箭头函数调用者为window,但原 fn 调用者可能不是window,所有需要保持原来的this指向)
timer = setTimeout(() => {
// 闭包来封装定时器变量,这样做可以保护定时器变量不被非法更改
console.log('定时器的 this', this);
// fn 函数就这样调用的话 this就是 window ,也没有事件源对象(严格模式下this指向 undefind) 所以直接调用 fn 是不行的。
// apply使 this 指向调用者,为了保证设置防抖之后响应事件内部的this指向和事件源对象 与未设置防抖之前保持一致
// arguments获取传入的参数(是一个伪数组,arguments指当前return这个函数接收的参数,该参数原本就是需要传给 fn 的 ) 。
fn.apply(this, arguments)
timer = null
}, delay);
}
};
原理其实很简单:
- 需要一个函数来接收要处理的函数 (
fn
)和节流的时间(delay
) - 返回一个处理之后(设置了节流)的函数
- 用一个定时器来实现延迟多少时间 且 执行传进来的函数(
fn
)的功能 - 注意
this
指向 和 接受参数 以及 清除定时器
- 用一个定时器来实现延迟多少时间 且 执行传进来的函数(
说到接收参数,很多小伙伴可能入使用的 rest
比较多
也可以这样写
function myDebounce(fn, delay) {
let timer = null;
return function (...args) {
if (timer) {
clearTimeout(timer);
};
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay);
}
};
dom.oninput = myDebounce(function (event) {
console.log(event);
}, 500);
手写节流
function myThrottle(fn, delay) {
let timer = null;
return function () {
// 这儿是重点 如果时间没到 那么 timer 就不为 null 就会返回 不会往下执行!!!
if (timer) {
return
};
timer = setTimeout(() => {
// 时间到了 timer 被设置为null 就走到这儿来了 就会执行
fn.apply(this, arguments)
timer = null
}, delay);
}
};
// 综合上面两步 就实现了 英雄 技能cd 一样的节流
一法通,万法通,万变不离其宗,只要掌握了核心科技,可以玩出花
时间戳版本
function myThrottle(fn, waitTime) {
var lastTime = 0;// 上一次的时间
return function () {
var nowTime = new Date().getTime(); // 获取当前时间戳
if (nowTime - lastTime > waitTime) {
// 当前时间减去上一次的时间,如果大于 waitTime(你设置的时间) 就可以再次操作了
fn.apply(this, arguments);
// 将当前时间复制给上一次的时间
lastTime = nowTime;
}
}
};
时间戳版本也是为了实现 一个时间间隔 也就是 上面所说的 规定的时间里只执行一次 因为每次执行完成之后上一次的时间 lastTime
都是刚刚的当前时间 nowTime
, nowTime - lastTime > waitTime
都明白吧?其实就是这个 ==> nowTime > lastTime + waitTime
意思就是 必须等 xx 秒 时间之后 你这个等式才能成立 然后才能执行
转载自:https://juejin.cn/post/7226157821707649085