zoukankan      html  css  js  c++  java
  • 解析underscore中的debounce

    先奉上源码

    取自Underscore.js 1.9.1的debounce

    _.debounce = function(func, wait, immediate) {
      var timeout, result;
    
      var later = function(context, args) {
        timeout = null;
        if (args) result = func.apply(context, args);
      };
    
      var debounced = restArguments(function(args) {
        if (timeout) clearTimeout(timeout);
        if (immediate) {
          var callNow = !timeout;
          timeout = setTimeout(later, wait);
          if (callNow) result = func.apply(this, args);
        } else {
          timeout = _.delay(later, wait, this, args);
        }
    
        return result;
      });
    
      debounced.cancel = function() {
        clearTimeout(timeout);
        timeout = null;
      };
    
      return debounced;
    };
    

    其中比较陌生的是restArguments_.delay,那么我们首先来逐个分析它们

    restArguments

    // Some functions take a variable number of arguments, or a few expected
    // arguments at the beginning and then a variable number of values to operate
    // on. This helper accumulates all remaining arguments past the function’s
    // argument length (or an explicit `startIndex`), into an array that becomes
    // the last argument. Similar to ES6’s "rest parameter".
    var restArguments = function(func, startIndex) {
      startIndex = startIndex == null ? func.length - 1 : +startIndex;
      return function() {
        var length = Math.max(arguments.length - startIndex, 0),
            rest = Array(length),
            index = 0;
        for (; index < length; index++) {
          rest[index] = arguments[index + startIndex];
        }
    
        // 个人觉得这段switch没有特别意义,可以删除
        // switch (startIndex) {
        //  case 0: return func.call(this, rest);
        //  case 1: return func.call(this, arguments[0], rest);
        // case 2: return func.call(this, arguments[0], arguments[1], rest);
        // }
    
        var args = Array(startIndex + 1);
        for (index = 0; index < startIndex; index++) {
          args[index] = arguments[index];
        }
        args[startIndex] = rest;
        return func.apply(this, args);
      };
    };
    

    它很类似ES6剩余参数
    举个例子

    function sum (a, b, rest) {
      var sum = a + b;
      console.log(Array.isArray(rest)); // 打印true
      if (rest.length) {
        sum += rest.reduce((x, y) => x + y);
      }
      return sum;
    }
    ra_sum = restArguments(sum);
    console.log(ra_sum(1, 2)); // 8
    console.log(ra_sum(1, 2, 3, 4, 5)); // 15
    
    // 利用ES6的剩余参数可以这样写
    function es6_ra_sum(a, b, ...rest) {
      var sum = a + b;
      console.log(rest)
      console.log(Array.isArray(rest)); // 打印true
      if (rest.length) {
        sum += rest.reduce((x, y) => x + y);
      }
      return sum;
    }
    console.log(es6_ra_sum(1, 2)); // 3
    console.log(es6_ra_sum(1, 2, 3, 4, 5)); // 15
    

    _.delay

    // Delays a function for the given number of milliseconds, and then calls
    // it with the arguments supplied.
    _.delay = restArguments(function(func, wait, args) {
      return setTimeout(function() {
        return func.apply(null, args);
      }, wait);
    });
    
    // 相当于
    _.delay = function(func, wait, ...args) {
      return setTimeout(function() {
        return func.apply(null, args);
      }, wait);
    }
    

    _.debounce

    _.debounce = function(func, wait, immediate) {
      var timeout, result;
    
      var later = function(context, args) {
        timeout = null; // 重置timeout为了leading执行
    
        // 判断arg是为了下面运行timeout = setTimeout(later, wait);这句话时func不会被执行
        if (args) result = func.apply(context, args);
      };
    
      // 原本来是restArgumenst返回函数,这里为了直观我直接换成es6的剩余参数形式
      var debounced = function(...args) {
        if (timeout) clearTimeout(timeout);
    
        if (immediate) {
          //初始的时候timeout为undefined,later函数运行的时候置为null, 这两种情况callNow为true
          var callNow = !timeout;
    
          // 下面这句话的目的不是为了执行func而是切换timeout的值,也就是间接改变callNow。而且later中args并没有传入所以不会执行later中不会执行func
          timeout = setTimeout(later, wait);
    
          // 这句话才是当immediate为true时真正地执行func
          if (callNow) result = func.apply(this, args);
        } else {
          // trailing执行func
          timeout = _.delay(later, wait, this, args);
          // 相当于setTimeout(function() {
          //    return later.apply(null, [this, args]);
          // }, wait);
          // 再在later中运行result = func.apply(this, args); 最后和callNow的时候运行一致
        }
        return result;
      }
    
      debounced.cancel = function() {
        clearTimeout(timeout);
        timeout = null;
      };
    
      return debounced;
    };
    
  • 相关阅读:
    idea配置SOLServer错误解决记录
    精确的double加减乘除运算工具类
    Java类型转换工具类(十六进制—bytes互转、十进制—十六进制互转,String—Double互转)
    rest的Web服务端获取http请求头字段
    前端开发规范:1-通用规范
    一些webpack常见编译报错的解决方案
    常用的数组对象操作方法
    理解ES6的新数据类型:Symbol
    canvas在vue中的应用
    vue-cli3+typescript+路由懒加载报错问题
  • 原文地址:https://www.cnblogs.com/guanine/p/9597826.html
Copyright © 2011-2022 走看看