cobish / cobish.github.io

cobish's blog
https://cobish.github.io
178 stars 27 forks source link

函数防抖与函数节流 #4

Open cobish opened 8 years ago

cobish commented 8 years ago

我们在开发中都会遇到这样一种情况:先给 Window 绑定一个 scroll 事件,然后打印日志,代码如下。打开浏览器,滚动一下鼠标,便会发现日志被频繁地打印出来。类似这样的事件还是 Window 的 resize 事件、输入框的 keyup 事件、拖拽时的 mousemove 事件等等。

$(window).on('scroll', function() {
  console.log(111);
});

它们都有以下两个特点:

既然无法对这些事件进行改动,那么我们只能在被调用的函数中想办法优化了。因为使用场景的不同,所以有 函数防抖函数节流 这两种优化方案,下面将逐一进行介绍。

函数防抖

如果用手指一直按住一个弹簧,它将不会弹起直到你松手。函数防抖 debounce 就是给函数设置一个定时器,n 秒之后才调用函数,n 秒内如果再次设置定时器的话,则会重新定时 n 秒后执行函数。

函数防抖的简单实现:

/**
 * 函数防抖
 * @param  {Function} 调用的函数
 * @param  {Int}      时间
 * @return {Function} 返回客户调用函数
 */
var debounce = function(action, time) {
  var timer = null;

  return function() {
    var context = this, args = arguments;

    clearTimeout(timer);

    timer = setTimeout(function() {
      action.apply(context, args);
    }, time);
  };
};

这时如果我们再结合 Window 的 scroll 事件,可以发现控制台里的打印明细打印少了很多,而且几乎是在停止 scroll 了之后才会去调用 doResize 函数(主要是因为设置的事件为 500 毫秒)。

function doResize() {
  console.log(arguments);
}

var action = debounce(doResize, 500);
$(window).on('resize', action);

函数节流

如果将水龙头拧紧直到水是以水滴的形式流出,那你会发现每隔一段时间,就会有一滴水流出。函数节流 throttle 顾名思义就是节约使用函数次数的意思。

有一种场景,一个输入框在 keyup 之后需获取到输入的值去 ajax 请求,如果是正常实现的话,那 ajax 请求的数量则太大了。如果是使用 debounce 的话,则会在用户输入完毕后的 n 秒后才会去 ajax 请求。我们需要的只是减少 keyup 事件的触发,而不是完全禁止它等到最后一个 keyup 才触发。这时我们就可以使用函数节流throttle

函数节流的简单实现:

/**
 * 函数节流
 * @param  {Function} 调用的函数
 * @param  {Int}      时间,单位毫秒
 * @return {Function} 返回客户调用函数
 */
var throttle = function(action, delay) {
  var last = 0;
  return function() {
    var curr = +new Date();
    if (curr - last > delay) {
      action.apply(this, arguments);
      last = curr;
    }
  };
}

再结合输入框的 keyup 事件,就可以看出 keyup 调用的频率明显减少了。

function doInput() {
  console.log($(this).val());
}

var action = throttle(doInput, 1000);
$('#txt').on('keyup', action);

使用 underscore.js

underscore.js 有对 debouncethrottle 的分别实现。将上面的代码修改如下:

// debounce
var resizeAction = _.debounce(doResize, 500);
$(window).on('resize', resizeAction);

// throttle
var keyupAction = _.throttle(doInput, 1000);
$('#txt').on('keyup', keyupAction);

参考

zhengyuefeng commented 8 years ago

很棒