• 防抖(debounce)和 节流(throttling)


    防抖(debounce)和 节流(throttling)

    1、防抖和节流出现的原因

    防抖和节流是针对响应跟不上触发频率这类问题的两种解决方案。

    • 在给DOM绑定事件时,有些事件我们是无法控制触发频率的。 如鼠标移动事件onmousemove, 滚动滚动条事件onscroll,窗口大小改变事件onresize,瞬间的操作都会导致这些事件会被高频触发。 如果事件的回调函数较为复杂,就会导致响应跟不上触发,出现页面卡顿,假死现象。

    • 在实时检查输入时,如果我们绑定onkeyup事件发请求去服务端检查,用户输入过程中,事件的触发频率也会很高,会导致大量的请求发出,响应速度会大大跟不上触发。

    ps:防抖和节流的作用都是防止函数多次调用。区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于wait,防抖的情况下只会调用一次,而节流的 情况会每隔一定时间(参数wait)调用函数。

    防抖

    实现简单防抖

    
    function debounce(fn, wait) {
        // 缓存定时器
        var timer = null;    
        return function(...args) {
            if (timer) clearTimeout(timer)
                timer = setTimeout(() => {
                fn.apply(this, args)
            }, wait)
    	}
    }
    
    // 处理函数
    function handle() {    
    	console.log('测试防抖'); 
    }
    
    // 滚动事件
    window.addEventListener('scroll', debounce(handle, 1000));
    
    

    上面的防抖函数没有立即执行事件,

    实现带有立即执行的防抖函数

    
    /**
     * 防抖函数,返回函数连续调用时,空闲时间必须大于或等于 wait,func 才会执行
     *
     * @param  {function} func        回调函数
     * @param  {number}   wait        表示时间窗口的间隔
     * @param  {boolean}  immediate   设置为ture时,是否立即调用函数
     * @return {function}             返回客户调用函数
     */
    function debounce (func, wait = 50, immediate = true) {
      let timer, context, args;
    
      // 延迟执行函数
      const later = () => setTimeout(() => {
          timer = null;
        // 延迟执行的情况下,函数会在延迟函数中执行
        // 使用到之前缓存的参数和上下文
        if (!immediate) {
            func.apply(context, args)
            context = args = null
        }
       
      },wait)
    
       return function(...params) {
        // 如果没有创建延迟执行函数(later),就创建一个
        if (!timer) {
          timer = later()
          // 如果是立即执行,调用函数
          // 否则缓存参数和调用上下文
          if (immediate) {
            func.apply(this, params)
          } else {
            context = this
            args = params
          }
        // 如果已有延迟执行函数(later),调用的时候清除原来的并重新设定一个
        // 这样做延迟函数会重新计时
        } else {
          clearTimeout(timer)
          timer = later()
        }
      }
    }
    
    

    节流

    防抖动和节流本质是不一样的。防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。

    实现节流

    
    /**
     * underscore 节流函数,返回函数连续调用时,func 执行频率限定为 次 / wait
     *
     * @param  {function}   func      回调函数
     * @param  {number}     wait      表示时间窗口的间隔
     * @param  {object}     options   如果想忽略开始函数的的调用,传入{leading: false}。
     *                                如果想忽略结尾函数的调用,传入{trailing: false}
     *                                两者不能共存,否则函数不能执行
     * @return {function}             返回客户调用函数
     */
    _.throttle = function(func, wait, options) {
        var context, args, result;
        var timeout = null;
        // 之前的时间戳
        var previous = 0;
        // 如果 options 没传则设为空对象
        if (!options) options = {};
        // 定时器回调函数
        var later = function() {
          // 如果设置了 leading,就将 previous 设为 0
          // 用于下面函数的第一个 if 判断
          previous = options.leading === false ? 0 : _.now();
          // 置空一是为了防止内存泄漏,二是为了下面的定时器判断
          timeout = null;
          result = func.apply(context, args);
          if (!timeout) context = args = null;
        };
        return function() {
          // 获得当前时间戳
          var now = _.now();
          // 首次进入前者肯定为 true
    	  // 如果需要第一次不执行函数
    	  // 就将上次时间戳设为当前的
          // 这样在接下来计算 remaining 的值时会大于0
          if (!previous && options.leading === false) previous = now;
          // 计算剩余时间
          var remaining = wait - (now - previous);
          context = this;
          args = arguments;
          // 如果当前调用已经大于上次调用时间 + wait
          // 或者用户手动调了时间
     	  // 如果设置了 trailing,只会进入这个条件
    	  // 如果没有设置 leading,那么第一次会进入这个条件
    	  // 还有一点,你可能会觉得开启了定时器那么应该不会进入这个 if 条件了
    	  // 其实还是会进入的,因为定时器的延时
    	  // 并不是准确的时间,很可能你设置了2秒
    	  // 但是他需要2.2秒才触发,这时候就会进入这个条件
          if (remaining <= 0 || remaining > wait) {
            // 如果存在定时器就清理掉否则会调用二次回调
            if (timeout) {
              clearTimeout(timeout);
              timeout = null;
            }
            previous = now;
            result = func.apply(context, args);
            if (!timeout) context = args = null;
          } else if (!timeout && options.trailing !== false) {
            // 判断是否设置了定时器和 trailing
    	    // 没有的话就开启一个定时器
            // 并且不能不能同时设置 leading 和 trailing
            timeout = setTimeout(later, remaining);
          }
          return result;
        };
      };
    
    

    本文主要参考:前端面试之道

  • 相关阅读:
    登录界面点击登录后如何延迟提示成功的div的显示时间并跳转
    关于如何用jq定位到某个元素的索引
    总结React关于require的问题
    关于React的赋值与调用方法
    React项目搭建(脚手架)
    关于th,td,tr的一些相关标签
    一个IP多个https站点配置
    ubuntu配置apache的虚拟主机
    putty如何使用
    CI基本配置
  • 原文地址:https://www.cnblogs.com/shengmo/p/11423435.html
Copyright © 2020-2023  润新知