节流和防抖相信很多同学已经在很多场景使用到了,如果你还不知道是什么或者知道但是不知道如何去实现(记不住),那么请继续往下阅读,本次我们只讲节流。
先简单介绍一下节流throttle:在触发任务的时候执行任务,并在接下来的一段时间(delay)内再次触发将不再执行。
举个浅显易懂的🌰:打王者荣耀的时候,我们按下下普通🐓键(实际上我们的手指疯狂点击),这时候角色会攻击出第一下,在某个间隔里面,我们的疯狂点击实际只有第一下生效了
以上就是所谓的节流了,接下来我们来看看怎么实现
1、一步步来,首先我们需要定义一个函数,这个函数接收两个参数 (fn:需要节流的函数,delay:需要延迟的时间),并且为了外部能够调用,这个函数返回一个函数
// 接受两个入参
function throttle(fn, delay){
// 返回函数供外部调用
return function() {
}
}
2、接着是第一次调用直接执行
// 接受两个入参
function throttle(fn, delay){
// 返回函数供外部调用
return function() {
fn()
}
}
3、很明显上面代码每次调用都会执行,那么我们需要记录过去执行的时间记为last,
// 接受两个入参
function throttle(fn, delay){
// 利用闭包,让外部调用的时候能够访问到同一个last变量
let last = null
// 返回函数供外部调用
return function() {
fn()
}
}
4、现在我们有了last,初始为空,就可以开始写我们的逻辑了(无非就是控制什么时间点执行函数
// 接受两个入参
function throttle(fn, delay){
// 记录上一次调用的时间,利用闭包,让外部调用的时候能够访问到同一个last变量
let last
// 返回函数供外部调用
return function() {
// 记录当前时间
let now = +new Date() // 取时间戳
// 第一次调用直接执行 || 上一次执行的时间已经距离现在>delay
if(!last || last+delay<now) {
last = now // 更新last为执行的时间
fn()
}
}
}
以上,一个最简单的节流函数就实现了(实际代码不过10行)
我们在浏览器上就可以通过以下代码实现一个带节流的轮询了
let ajax = throttle(()=>{console.log(’节流’)}, 3000); setInterval(ajax, 1000)
5、接下来是优化,我们的函数可能需要传参,以上面ajax为例,让下面的代码输出 ”节流”
let ajax = throttle((name)=>{console.log(name)}, 3000); setInterval(ajax('节流'), 1000)
// 接受两个入参
function throttle(fn, delay){
// 记录上一次调用的时间,利用闭包,让外部调用的时候能够访问到同一个last变量
let last
// 返回函数供外部调用
return function() {
// 函数的入参在这里
let arg = arguments
// 记录当前时间
let now = +new Date() // 取时间戳
// 第一次调用直接执行 || 上一次执行的时间已经距离现在>delay
if(!last || last+delay<now) {
last = now // 更新last为执行的时间
fn(...arg)
}
}
}
6、最后我们让初始函数的this指向调用该函数的对象
// 接受两个入参
function throttle(fn, delay){
// 记录上一次调用的时间,利用闭包,让外部调用的时候能够访问到同一个last变量
let last
// 返回函数供外部调用
return function() {
// 函数的入参在这里
let arg = arguments
// 存下当前的this
let self = this
// 记录当前时间
let now = +new Date() // 取时间戳
// 第一次调用直接执行 || 上一次执行的时间已经距离现在>delay
if(!last || last+delay<now) {
last = now // 更新last为执行的时间
fn.call(self,...arg)
}
}
}
循序渐进,很容易就能手写出来了
扩展需求:要求最后一次点击之后,等计时器结束再执行一遍
7、这个时候我们的计时器就派上用场啦,给代码亿丢丢小小的改造
// 接受两个入参
function throttle(fn, delay){
// 记录上一次调用的时间,利用闭包,让外部调用的时候能够访问到同一个last变量
let last
let timer // 添加定时器
// 返回函数供外部调用
return function() {
// 函数的入参在这里
let arg = arguments
// 存下当前的this
let self = this
// 记录当前时间
let now = +new Date() // 取时间戳
// 第一次调用直接执行 || 上一次执行的时间已经距离现在>delay
// if(!last || last+delay<now) {
// last = now // 更新last为执行的时间
// fn.apply(self,arg)
// }
*// 在时间间隔之内调用,清空并重新打开定时器,
if(last && now<last+delay){
clearTimeout(timer)
timer = setTimeout(function(){
last = now
fn.call(self,...arg)
},delay)
}else{
// 超过时间间隔的调用,直接执行
last = now
fn.call(self,...arg)
}*
}
}
以上,我们通过一步步完善最终实现了一个节流函数,下一次再回想起来,按着思路也能轻松手写出来啦!