防抖和节流 && 手写js防抖和节流函数

防抖(debounce)

顾名思义:抖就是手抖,手抖会造成什么,造成点击某个窗口同一地方短时间内点击多次,具体点击多少次就看你手抖的程度了。 防抖就是为了避免这种情况的发生,保证这个函数在短时间内只执行最后一次。

实现原理: 函数被触发时,先延迟执行的时间,在延迟的时间内如果再次被触发,则取消之前的延迟,再重新开始新的一轮延迟,周而复始,达到只执行最后一次请求,其他请求则被过滤掉的效果。

手动实现一个防抖函数:

//fn为高频出发的函数,delay为延迟的时间
function debounce(fn,delay){
          
   
  var timer = null
  return function(){
          
   
    //清除之前的定时器
    clearTimeout(timer)
    //重新设置一个新的定时器
    timer = setTimeout(()=>{
          
   
      fn.call(this)
    },delay)
  }
}
//自定义一个函数a
function a(){
          
   
  console.log("hello yu")
}

//调用防抖函数
var b = debounce(a,3000)
for(let i=1;i<10;i++){
          
   
  b()
}

我本人看到这段代码的时候有两个疑惑点 1.var timer = null这句和clearTimeout(timer)两个不是冲突了吗,都初始化成null,为啥还清除。 2.fn.call(this)这是个啥玩意,直接fn()不行吗?

经过一番操作,才知其中关窍 1.我们的b()是指debounce函数返回的结果,也是一个函数,具体指的就是

return function(){
          
   
    //清除之前的定时器
    clearTimeout(timer)
    //重新设置一个新的定时器
    timer = setTimeout(()=>{
          
   
      fn.call(this)
    },delay)
  }

这个函数,就意味着var timer = null这句只执行一遍。

2.第二个问题是因为之所以要通过call方式来修改原函数的this,是因为原函数在通过参数传递时,只会被当成普通函数处理,不会去管原函数是否挂载在某个对象上,如果说原函数内部有this指向,如果直接使用fn(),会出现this指向错误的问题。

针对这个问题有两种解决办法

1.传递fn()原函数时手动进行显示绑定this

function debounce(fn,delay){
          
   
  var timer = null
  return function(){
          
   
    //清除之前的定时器
    clearTimeout(timer)
    //重新设置一个新的定时器
    timer = setTimeout(()=>{
          
   
      fn()
    },delay)
  }
}
var o = {
          
   
    c: 1,
    a: function() {
          
   
        console.log(this.c);
    }
}
var b = debounce(o.a.bind(o));

2.就在debounce函数内部通过apply或者call的方式调用原函数 但是使用这个方法有个前提就是如果原函数本来挂载在某个对象上,return的函数也需要挂载在那个对象上,这样才会保证this是同一个this

function debounce(fn,delay){
          
   
  var timer = null
  return function(){
          
   
    //清除之前的定时器
    clearTimeout(timer)
    //重新设置一个新的定时器
    timer = setTimeout(()=>{
          
   
      fn.call(this)
    },delay)
  }
}
var o = {
          
   
    c: 1,
    a: function() {
          
   
        console.log(this.c);
    }
}
var b = debounce(o.a);

这个地方如果写成var b = debounce(a)也会发生this指代错误。

节流(throttle)

节流和防抖本质上解决的问题是一样的,只是处理的方法是不一样的

实现原理: 函数被触发时,只响应在某个固定时间短内的第一次请求,后续的请求都不响应,直到下个时间周期,继续响应下个周期内的第一次请求,周而复始。

上代码:

//节流函数
function throttle(fn,delay){
          
   
  var lastTime = 0
  return function(){
          
   
    var nowTime = new Date().getTime()
    console.log(lastTime)
    if(nowTime - lastTime > delay){
          
   
      fn.call(this)
      lastTime = nowTime 
    }
  }
}

//自定义一个函数a
function a(){
          
   
  console.log("hello yu")
}

//调用节流函数
var b = debounce(a,3000)
for(let i=1;i<1000;i++){
          
   
  b()
}

是不是很简单呢~

经验分享 程序员 微信小程序 职场和发展