正文
最近在面试中遇到过几次手撕节流的情况,于是就研究了一下。
节流的话有三种写法,时间戳, setTimeOut以及综合起来的写法,三种写法各自有自己的特点,下面是三种代码的实现。
时间戳
function myTrottle(fn, delay) { // 开始
let startTime = 0
let context = this
let args = arguments
return function(){
let endTime = Date.now()
if(endTime - startTime > delay) {
fn.call(context, ...args)
startTime = endTime
}
}
}
时间戳的写法,开始会执行一次,当然,如果startTime
初始化为Date.now()
,开始的一次就不会执行。
定时器
function myTrottle(fn, delay) { // 开始 + 结束
let context = this
let args = arguments
let timer = null
return function(){
if(!timer){
timer = setTimeout(()=>{
fn.call(context, ...args)
timer = null // 这里是赋值 此时setTimeout依然在执行,只不过timer指向为null
},delay)
}
}
}
定时器的写法也会立即延迟执行一次,但是由于setTimeout本身的一些问题,延时并不准确,所以有了下面综合的写法。
综合写法
相对于时间戳的写法和定时器的写法,这个最终版综合了时间戳准确的特点,并且也能够像定时器一样执行最后一次。
function myTrottle(fn, delay) { // 开始 + 结束
let timer = null
let startTime = Date.now()
let context = this
let args = arguments
return function () {
let endTime = Date.now()
let de = delay - (endTime - startTime)
clearTimeout(timer) // 这里是clearTimeout,重置定时器
if(de < 0) {
startTime = endTime
fn.call(context, ...args)
} else{
timer = setTimeout(()=>{
fn.call(context, ...args)
},de)
}
}
}
在这段代码中,每次执行时如果时间差值大于delay
就会立即执行,并且除了最后一次执行,其他的都是走的时间戳,当停止调用的时候才会走定时器,从而做到最后一次也执行,并且更加准确。
附带一个防抖
function myDebounce(fn, delay) { // 第一次会执行
let context = this
let args = arguments
let timer = null
return function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.call(context, ...args)
},delay)
}
}