debounce 函数
有时,我们不希望回调函数被频繁调用。比如,用户填入网页输入框的内容,希望通过 Ajax 方法传回服务器,jQuery 的写法如下。

$('textarea').on('keydown', ajaxAction);

下面这种做法叫做 debounce(防抖动)。假定两次 Ajax 通信的间隔不得小于2500毫秒,上面的代码可以改写成下面这样。

$('textarea').on('keydown', debounce(ajaxAction, 2500));
 function debounce(fn, delay){
   var timer = null; // 声明计时器
   return function() {
     var context = this;
     var args = arguments;
     clearTimeout(timer);
     timer = setTimeout(function () {
       fn.apply(context, args);
     }, delay);
   };
 }


上面代码中,只要在2500毫秒之内,用户再次击键,就会取消上一次的定时器,然后再新建一个定时器。这样就保证了回调函数之间的调用间隔,至少是2500毫秒。

window.addEventListener('scroll', callback);
scroll事件会连续地大量触发,所以它的监听函数之中不应该有非常耗费计算的操作。推荐的做法是使用requestAnimationFrame或setTimeout控制该事件的触发频率,然后可以结合customEvent抛出一个新事件。

(function () {
   var throttle = function (type, name, obj) {
     var obj = obj || window;
     var running = false;
     var func = function () {
       if (running) { return; }
       running = true;
       requestAnimationFrame(function() {
         obj.dispatchEvent(new CustomEvent(name));
         running = false;
       });
     };
     obj.addEventListener(type, func);
   };  // 将 scroll 事件重定义为 optimizedScroll 事件
   throttle('scroll', 'optimizedScroll');
 })();window.addEventListener('optimizedScroll', function() {
   console.log('Resource conscious scroll callback!');
 });


上面代码中,throttle函数用于控制事件触发频率,requestAnimationFrame方法保证每次页面重绘(每秒60次),只会触发一次scroll事件的监听函数。也就是说,上面方法将scroll事件的触发频率,限制在每秒60次。具体来说,就是scroll事件只要频率低于每秒60次,就会触发optimizedScroll事件,从而执行optimizedScroll事件的监听函数。

改用setTimeout方法,可以放置更大的时间间隔。

(function() {
   window.addEventListener('scroll', scrollThrottler, false);  var scrollTimeout;
   function scrollThrottler() {
     if (!scrollTimeout) {
       scrollTimeout = setTimeout(function () {
         scrollTimeout = null;
         actualScrollHandler();
       }, 66);
     }
   }  function actualScrollHandler() {
     // ...
   }
 }());


上面代码中,每次scroll事件都会执行scrollThrottler函数。该函数里面有一个定时器setTimeout,每66毫秒触发一次(每秒15次)真正执行的任务actualScrollHandler。

下面是一个更一般的throttle函数的写法。

function throttle(fn, wait) {
   var time = Date.now();
   return function() {
     if ((time + wait - Date.now()) < 0) {
       fn();
       time = Date.now();
     }
   }
 }window.addEventListener('scroll', throttle(callback, 1000));


上面的代码将scroll事件的触发频率,限制在一秒一次。

lodash函数库提供了现成的throttle函数,可以直接使用。

window.addEventListener('scroll', _.throttle(callback, 1000));

前面介绍过debounce的概念,throttle与它区别在于,throttle是“节流”,确保一段时间内只执行一次,而debounce是“防抖”,要连续操作结束后再执行。以网页滚动为例,debounce要等到用户停止滚动后才执行,throttle则是如果用户一直在滚动网页,那么在滚动过程中还是会执行。