防抖、节流函数

参考文章: https://www.cnblogs.com/cc-freiheit/p/10827372.html

防抖

  1. 短时间内多次触发同一事件,只执行最后一次,或者只执行最开始的一次,中间的不执行。
  2. 分为立即执行和非立即执行;

非立即执行;

非立即执行版的意思是触发事件后函数不会立即执行,而是在 n 秒后执行,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

function debounce(func, wait) {
    let timer;
    return function() {
      let context = this; // 注意 this 指向
      let args = arguments; // arguments中存着e
         
      if (timer) clearTimeout(timer);
 
      timer = setTimeout(() => {
        func.apply(this, args)
      }, wait)
    }
}

function count(a, b) {
	console.log(a, b)
}

let debounceCount = debounce(count,1000);
setInterval(() => {
     debounceCount(1, 2)
}, 100);

代码解析:

  1. debounce函数return一个新的函数,利用arguments将debounceCount的参数传给func(count)中
  2. debounce函数作用域存着一个timer(闭包),每次调用debounceCount时如果有timer就clear掉,然后延期执行func.apply(this, args);
  3. 这儿胡this指向哪儿呢? 指向的是debounceCount函数执行的作用域, 也就是外部作用域。那么为什么要this呢? 不太清楚。 嗯 , 知道了, 是为了content.onmousemove 类似的指向对象的,同时, 也为了在debounceCount中可以访问外部的变量, 没有this,就访问不到了。

立即执行

立即执行版的意思是触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果

// 合成版
/**
   * @desc 函数防抖
   * @param func 目标函数
   * @param wait 延迟执行毫秒数
   * @param immediate true - 立即执行, false - 延迟执行
   */
function debounce(func, wait, immediate) {
    let timer;
    return function() {
      let context = this,
          args = arguments;
           
      if (timer) clearTimeout(timer);
      if (immediate) {
        let callNow = !timer;
        if (timer) clearTimeout(timer);
        timer = setTimeout(() => {
          timer = null;
        }, wait);
        if (callNow) func.apply(context, args);
      } else {
        timer  = setTimeout(() => {
          func.apply
        }, wait)
      }
    }
}

代码解析:

  1. 大致和非立即执行差不多,函数传入参数为立即执行时,如果callNow 执行,那么合适为callNow为true呢? 没有timer的时候, 也就是说有timer就不执行,首次调用没timer,在有timer的时候调用将更新timer = null的延迟, wait秒后, timer = null, 又变成首次的状态。

节流(throttle)

  1. 连续触发事件但是在 n 秒中只执行一次函数。即 2n 秒内执行 2 次… 。
  2. 时间戳和定时器版。

时间戳

在持续触发事件的过程中,函数会立即执行,并且每 1s 执行一次。最后一次可能会被过滤掉

// 时间戳版
function throttle(func, wait) {
    let previous = 0;
    return function() {
      let now = Date.now();
      let context = this;
      let args = arguments;
      if (now - previous > wait) {
        func.apply(context, args);
        previous = now;
      }
    }
}

代码解析:

  1. previous是上次的时间戳,返回一个函数, 如果now - previous > wait, 就可以执行func.apply(context, args); 同时更新previous为now,为下次做准备。

定时器版

在持续触发事件的过程中,函数不会立即执行,并且每 1s 执行一次,在停止触发事件后,函数还会再执行一次。

// 定时器版
function throttle(func, wait) {
    let timeout;
    return function() {
      let context = this;
      let args = arguments;
      if (!timeout) {
        timeout = setTimeout(() => {
          timeout = null;
          func.apply(context, args)
        }, wait)
      }
    }
}

代码解析:

  1. 方法延迟1s后执行, 延迟一秒后timer设置为null, 在有定时器期间, 不处理任何事情,
  2. 和防抖的区别是什么呢?非立即执行的防抖是,每次点击clearTimeout,直接更新timeout, 不加判断,这儿就加了个判断
  3. 那么和立即执行函数的区别呢?立即执行函数也加了判断,立即执行函数是 先赋值一个callNow是上次的!timer,然后设置定时器将timer变为null,接着根据callNow判断要不要调用func, 那么可不可以
    先if (!timer) func.apply(context, args); 再 设置 timer 呢? 也就是下面的代码, 是可以的
if (!timer) func.apply(context, args);
// 还有这儿为什么不加clearTimeout呢?是不是之前的也不用加clear
// 经过测试不clear的话会执行两次func,timer = null不会清空上一个定时器, 设置timer为null后, 第二次还是会在设置timer为null,那么我们的func呢,
// timer 在1s内被设置为null, .5s时又调了一次, 1stimer会变成null, 1.5s他也会变成null, 所以定时器多了,后timer会被频繁的设置为null, 也就是func可以频繁的被执行。 他会所以这儿他有个bug应该clearTimeout
clearTimeout(timer)
timer = setTimeout(() => {
   timer = null;
 }, wait);
        
时间戳版本与定时器版本的结合, 也就是传参而已。。。
/**
 * @desc 函数节流
 * @param func 函数
 * @param wait 延迟执行毫秒数
 * @param type 1 表时间戳版,2 表定时器版
 */
function throttle(func, wait, type) {
  if (type === 1) {
    let previous = 0;
  } else if (type === 2) {
    let timeout;
  }
  return function() {
    let context = this;
    let args = arguments;
    if (type === 1) {
        let now = Date.now();
 
        if (now - previous > wait) {
          func.apply(context, args);
          previous = now;
        }
    } else if (type === 2) {
      if (!timeout) {
        timeout = setTimeout(() => {
          timeout = null;
          func.apply(context, args)
        }, wait)
      }
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值