【JS】手撕代码-防抖节流概念与实现

当我们监听一个input输入框的onChange事件的时候,我们发现用户每输入一个数值,onChange事件都会被不停调用,但是其实很多时候我们只关心用户最后输入的完整数据,不需要每次数值变化都调用change事件。还有相似的场景比如window的resize、scroll事件等,都是被触发非常频繁的事件,但是我们实际又不需要那么频繁的产生回调。

函数防抖(debounce)

当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。如下图,持续触发scroll事件时,并不执行handle函数,当1000毫秒内没有触发scroll事件时,才会延时触发scroll事件。

也就是说防抖可以拆解成这些特性

  1. 触发事件之后不会马上执行,而是会等n秒之后再执行
  2. 如果n秒之内不再触发事件,n秒之后就会调用回调。
  3. 如果在n秒的过程中,你一直在触发新的事件,那么会以最近一次触发的时间为准,重新计算n秒,n秒到了才会执行回调。
手写防抖

功能点:

  1. 可以延迟n秒之后再执行
  2. 上下文当中使用到this,不能改变this指向
  3. 可以获取到事件对象event
  4. 可以立刻执行事件
  5. 返回函数执行结果
  6. 可以取消防抖重新触发
function debounce(func,wait, immediate){
  var timeout,result;
  var debounced =  function(){
    let env = this;
    var args = arguments;
    if(timeout){
      clearTimeout(timeout);
    }
    if(immediate){
            // 如果已经执行过,不再执行
            var callNow = !timeout;
            timeout = setTimeout(function(){
                timeout = null;
            }, wait);
            if (callNow) result = func.apply(context, args)
    }  else {
     timeout = setTimeout(function(){
       func.apply(env,args);
     },wait);
    }
    return result;
  }
  debounced.cancel = function(){
      clearTimeout(timeout);
      timeout = null;
  }
  return debounced;
}

函数节流(throttle)

当持续触发事件时,保证一定时间段内只调用一次事件处理函数。节流通俗解释就比如我们水龙头放水,阀门一打开,水哗哗的往下流,秉着勤俭节约的优良传统美德,我们要把水龙头关小点,最好是如我们心意按照一定规律在某个时间间隔内一滴一滴的往下滴。如下图,持续触发scroll事件时,并不立即执行handle函数,每隔1000毫秒才会执行一次handle函数。

  1. 持续触发事件的时候,一段时间只调用一次事件处理函数
  2. 每间隔一段时间执行一次回调
  3. 首次可以执行也可以不执行

节流 vs 防抖

防抖侧重的是延迟事件执行
节流侧重的是在某段时间内规律执行

手撕节流

function throttle(func, wait, options) {
    var timeout, context, args, result;
    var previous = 0;
    if (!options) options = {};

    var later = function() {
        previous = options.leading === false ? 0 : new Date().getTime();
        timeout = null;
        func.apply(context, args);
        if (!timeout) context = args = null;
    };

    var throttled = function() {
        var now = new Date().getTime();
        if (!previous && options.leading === false) previous = now;
        var remaining = wait - (now - previous);
        context = this;
        args = arguments;
        if (remaining <= 0 || remaining > wait) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            previous = now;
            func.apply(context, args);
            if (!timeout) context = args = null;
        } else if (!timeout && options.trailing !== false) {
            timeout = setTimeout(later, remaining);
        }
    };
    return throttled;
}

JavaScript专题之跟着underscore学防抖
JS的防抖与节流
JavaScript专题之跟着 underscore 学节流

你可能感兴趣的:(【JS】手撕代码-防抖节流概念与实现)