首页 > 代码库 > lodash源码学习debounce,throttle

lodash源码学习debounce,throttle

函数去抖(debounce)和函数节流(throttle)通常是用于优化浏览器中频繁触发的事件,具体内容可以看这篇文章http://www.cnblogs.com/fsjohnhuang/p/4147810.html

直接看lodash中对应方法的实现

_.debounce(func, [wait=0], [options={}])

//debounce.jsvar isObject = require(‘./isObject‘),//是否是对象    now = require(‘./now‘),//获取当前时间    toNumber = require(‘./toNumber‘);//转为为数字var FUNC_ERROR_TEXT = ‘Expected a function‘;var nativeMax = Math.max,//原生最大值方法    nativeMin = Math.min;//原生最小值方法/** * 函数去抖,也就是说当调用动作n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。 * * @param {Function} func 需要去抖的函数. * @param {number} [wait=0] 延迟执行的时间. * @param {Object} [options={}] 选项对象. * @param {boolean} [options.leading=false] 指定是否在超时前调用. * @param {number} [options.maxWait] func延迟调用的最大时间. * @param {boolean} [options.trailing=true] 指定是否在超时后调用. * @returns {Function} 返回去抖之后的函数. * @example * * // Avoid costly calculations while the window size is in flux. * jQuery(window).on(‘resize‘, _.debounce(calculateLayout, 150)); * * // Invoke `sendMail` when clicked, debouncing subsequent calls. * jQuery(element).on(‘click‘, _.debounce(sendMail, 300, { *   ‘leading‘: true, *   ‘trailing‘: false * })); * * // Ensure `batchLog` is invoked once after 1 second of debounced calls. * var debounced = _.debounce(batchLog, 250, { ‘maxWait‘: 1000 }); * var source = new EventSource(‘/stream‘); * jQuery(source).on(‘message‘, debounced); * * // Cancel the trailing debounced invocation. * jQuery(window).on(‘popstate‘, debounced.cancel); */function debounce(func, wait, options) {  var lastArgs,    //上次调用参数      lastThis,    //上次调用this      maxWait,    //最大等待时间      result,    //返回结果      timerId,    //timerId      lastCallTime,    //上次调用debounced时间,即触发时间,不一定会调用func      lastInvokeTime = 0, //上次调用func时间,即成功执行时间      leading = false,    //超时之前      maxing = false,     //是否传入最大超时时间      trailing = true;    //超时之后  if (typeof func != ‘function‘) {    throw new TypeError(FUNC_ERROR_TEXT);  }  wait = toNumber(wait) || 0;  if (isObject(options)) {    leading = !!options.leading;    maxing = ‘maxWait‘ in options;    maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;    trailing = ‘trailing‘ in options ? !!options.trailing : trailing;  }  function invokeFunc(time) {//调用func,参数为当前时间    var args = lastArgs,//调用参数        thisArg = lastThis;//调用的this    lastArgs = lastThis = undefined;//清除lastArgs和lastThis    lastInvokeTime = time;    //上次调用时间为当前时间    result = func.apply(thisArg, args);//调用func,并将结果返回    return result;  }  function leadingEdge(time) {//超时之前调用    lastInvokeTime = time;//设置上次调用时间为当前时间    timerId = setTimeout(timerExpired, wait); //开始timer    return leading ? invokeFunc(time) : result;//如果leading为true,调用func,否则返回result  }  function remainingWait(time) {//设置还需要等待的时间    var timeSinceLastCall = time - lastCallTime,//距离上次触发的时间        timeSinceLastInvoke = time - lastInvokeTime,//距离上次调用func的时间        result = wait - timeSinceLastCall;//还需要等待的时间    return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;  }  function shouldInvoke(time) {//是否应该被调用    var timeSinceLastCall = time - lastCallTime,//距离上次触发时间的时间        timeSinceLastInvoke = time - lastInvokeTime;//距离上次调用func的时间       return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||      (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));  }  function timerExpired() {//刷新timer    var time = now();    if (shouldInvoke(time)) {//如果可以调用,调用trailingEdge      return trailingEdge(time);    }    timerId = setTimeout(timerExpired, remainingWait(time));//不调用则重置timerId  }  function trailingEdge(time) {//超时之后调用    timerId = undefined;    // Only invoke if we have `lastArgs` which means `func` has been    // debounced at least once.    if (trailing && lastArgs) {//如果设置trailing为true,并且有lastArgs,调用func      return invokeFunc(time);    }    lastArgs = lastThis = undefined;//清除lastArgs和lastThis    return result;//否则返回result  }  function cancel() {//取消执行    if (timerId !== undefined) {      clearTimeout(timerId);    }    lastInvokeTime = 0;    lastArgs = lastCallTime = lastThis = timerId = undefined;  }  function flush() {//直接执行    return timerId === undefined ? result : trailingEdge(now());  }  function debounced() {    var time = now(),        isInvoking = shouldInvoke(time);//判断是否可以调用    lastArgs = arguments;//得到参数    lastThis = this;//得到this对象    lastCallTime = time;//触发时间    if (isInvoking) {      if (timerId === undefined) {//首次触发,调用leadingEdge        return leadingEdge(lastCallTime);      }      if (maxing) {        // 处理多次频繁的调用        timerId = setTimeout(timerExpired, wait);//设置定时器        return invokeFunc(lastCallTime);      }    }    if (timerId === undefined) {//如果没有timer,设置定时器      timerId = setTimeout(timerExpired, wait);    }    return result;//返回result  }  debounced.cancel = cancel;  debounced.flush = flush;  return debounced;}module.exports = debounce;

_.throttle(func, [wait=0], [options={}])

//throttle.jsvar debounce = require(‘./debounce‘),//debounce方法    isObject = require(‘./isObject‘);//判断是否为对象var FUNC_ERROR_TEXT = ‘Expected a function‘;/** * 函数节流 * * @param {Function} func 需要处理的函数. * @param {number} [wait=0] 执行间隔. * @param {Object} [options={}] 选项对象. * @param {boolean} [options.leading=false] 指定是否在超时前调用. * @param {number} [options.maxWait] func延迟调用的最大时间. * @param {boolean} [options.trailing=true] 指定是否在超时后调用. * @returns {Function} 返回节流之后的函数. * @example * * // Avoid excessively updating the position while scrolling. * jQuery(window).on(‘scroll‘, _.throttle(updatePosition, 100)); * * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. * var throttled = _.throttle(renewToken, 300000, { ‘trailing‘: false }); * jQuery(element).on(‘click‘, throttled); * * // Cancel the trailing throttled invocation. * jQuery(window).on(‘popstate‘, throttled.cancel); */function throttle(func, wait, options) {  var leading = true,      trailing = true;  if (typeof func != ‘function‘) {    throw new TypeError(FUNC_ERROR_TEXT);  }  if (isObject(options)) {    leading = ‘leading‘ in options ? !!options.leading : leading;    trailing = ‘trailing‘ in options ? !!options.trailing : trailing;  }  return debounce(func, wait, {    ‘leading‘: leading,    ‘maxWait‘: wait,    ‘trailing‘: trailing  });}module.exports = throttle;

可以看到这两个方法基本上都差不多,区别在于throttle初始的时候设置了leading为true和maxWait,这样和debounce的区别在于,在第一次触发的时候throttle会直接调用,并且每隔wait的时间都会调用一次,而debounce第一次不会调用,并且只有当触发的间隔时间大于wait时才会调用,否则一直不会调用。

 

lodash源码学习debounce,throttle