节流

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

  1. /**
  2. * underscore 节流函数,返回函数连续调用时,func 执行频率限定为 次 / wait
  3. *
  4. * @param {function} func 回调函数
  5. * @param {number} wait 表示时间窗口的间隔
  6. * @param {object} options 如果想忽略开始函数的的调用,传入{leading: false}。
  7. * 如果想忽略结尾函数的调用,传入{trailing: false}
  8. * 两者不能共存,否则函数不能执行
  9. * @return {function} 返回客户调用函数
  10. */
  11. _.throttle = function(func, wait, options) {
  12. var context, args, result;
  13. var timeout = null;
  14. // 之前的时间戳
  15. var previous = 0;
  16. // 如果 options 没传则设为空对象
  17. if (!options) options = {};
  18. // 定时器回调函数
  19. var later = function() {
  20. // 如果设置了 leading,就将 previous 设为 0
  21. // 用于下面函数的第一个 if 判断
  22. previous = options.leading === false ? 0 : _.now();
  23. // 置空一是为了防止内存泄漏,二是为了下面的定时器判断
  24. timeout = null;
  25. result = func.apply(context, args);
  26. if (!timeout) context = args = null;
  27. };
  28. return function() {
  29. // 获得当前时间戳
  30. var now = _.now();
  31. // 首次进入前者肯定为 true
  32. // 如果需要第一次不执行函数
  33. // 就将上次时间戳设为当前的
  34. // 这样在接下来计算 remaining 的值时会大于0
  35. if (!previous && options.leading === false) previous = now;
  36. // 计算剩余时间
  37. var remaining = wait - (now - previous);
  38. context = this;
  39. args = arguments;
  40. // 如果当前调用已经大于上次调用时间 + wait
  41. // 或者用户手动调了时间
  42. // 如果设置了 trailing,只会进入这个条件
  43. // 如果没有设置 leading,那么第一次会进入这个条件
  44. // 还有一点,你可能会觉得开启了定时器那么应该不会进入这个 if 条件了
  45. // 其实还是会进入的,因为定时器的延时
  46. // 并不是准确的时间,很可能你设置了2秒
  47. // 但是他需要2.2秒才触发,这时候就会进入这个条件
  48. if (remaining <= 0 || remaining > wait) {
  49. // 如果存在定时器就清理掉否则会调用二次回调
  50. if (timeout) {
  51. clearTimeout(timeout);
  52. timeout = null;
  53. }
  54. previous = now;
  55. result = func.apply(context, args);
  56. if (!timeout) context = args = null;
  57. } else if (!timeout && options.trailing !== false) {
  58. // 判断是否设置了定时器和 trailing
  59. // 没有的话就开启一个定时器
  60. // 并且不能不能同时设置 leading 和 trailing
  61. timeout = setTimeout(later, remaining);
  62. }
  63. return result;
  64. };
  65. };