JS函数节流,去抖

498 阅读4分钟

原文链接: 快速介绍几个JS函数

JS中函数是一等公民,地位很高。本文介绍快速介绍三个函数:

  • throttle 节流函数
  • debounce 去抖函数
  • compose 组合函数,这函数就是把一系列函数按顺序一个个执行。

节流和去抖函数

当某些事件频繁触发造成不断执行DOM操作或资源加载等行为,这时你可能就会想到用节流和去抖函数来处理。

throttle 节流函数

节流函数就是预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期。

可以用在下面两个情景中:

  • window对象的resize、scroll事件
  • 拖拽时的mousemove事件

简单实现

functionthrottle (fn, delay) {
    let last = 0
    return(...args) => {
        const now = Date.now()
        if (now - last > delay) {
            fn.call(this, args)
            last = now
        }
    }
}

debounce 去抖函数

去抖函数就是调用动作n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间。

通常用在下面的场景中:

  • 文字输入、自动完成的keyup事件

简单实现

functiondebounce (fn, delay) {
    let last = 0
    return(...args) => {
        clearTimeout(last)
        last = setTimeout(() => {
            fn.call(this, args)
        }, delay)
    }
}

这边找了一个网上的实现版本:

/*
* 频率控制 返回函数连续调用时,fn 执行频率限定为每多少时间执行一次
* @param fn {function}  需要调用的函数
* @param delay  {number}    延迟时间,单位毫秒
* @param immediate  {bool} 给 immediate参数传递false 绑定的函数先执行,而不是delay后后执行。
* @return {function}实际调用函数
*/var throttle = function (fn,delay, immediate, debounce) {
   var curr = +newDate(),//当前事件
       last_call = 0,
       last_exec = 0,
       timer = null,
       diff, //时间差
       context,//上下文
       args,
       exec = function () {
           last_exec = curr;
           fn.apply(context, args);
       };
   returnfunction () {
       curr= +newDate();
       context = this,
       args = arguments,
       diff = curr - (debounce ? last_call : last_exec) - delay;
       clearTimeout(timer);
       if (debounce) {
           if (immediate) {
               timer = setTimeout(exec, delay);
           } elseif (diff >= 0) {
               exec();
           }
       } else {
           if (diff >= 0) {
               exec();
           } elseif (immediate) {
               timer = setTimeout(exec, -diff);
           }
       }
       last_call = curr;
   }
};
 
/*
* 空闲控制 返回函数连续调用时,空闲时间必须大于或等于 delay,fn 才会执行
* @param fn {function}  要调用的函数
* @param delay   {number}    空闲时间
* @param immediate  {bool} 给 immediate参数传递false 绑定的函数先执行,而不是delay后后执行。
* @return {function}实际调用函数
*/var debounce = function (fn, delay, immediate) {
   return throttle(fn, delay, immediate, true);
};

自己画了一张图便于理解,最左边的boolean值是上边的immediate值,沿x轴的线是时间线,时间线上面是用户触发的动作,下面是函数的执行。

上面函数有点奇怪的地方就是immediate参数传递false 绑定的函数先执行。 ![](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="796" height="777"></svg>) 先看debounceimmediate不同的情况下的表现:

immediatefalse的情况下,第一次触发动作,函数就马上被执行了,后续的在delay时间段内重复按键都不会触发函数,而且会重置delay开始计算的时间点。当触发动作间隔时间超过delay后,又回到了初始状态,一触发动作就执行函数。

immediatetrue的情况下,触发动作后,函数并没有马上执行,只有在经过delay时间段内,没有触发动作时,函数才会被执行。

接着看throttleimmediate不同的情况下的表现:

immediatefalse的情况下,第一次触发动作,函数就马上被执行了,在这次执行后,在后续的delay时间段内触发动作都不会执行函数,间隔大于delay后,回到初始状态,触发动作函数就执行。

immediatetrue的情况下,第一次触发动作,函数就马上被执行了,同时定时器设置延时delay时间后在执行函数。其实和上面情况差不多,只不过会在delay后多执行一次。当然还有一些细节不同,图中可以看到,有两个执行之间时间并没有间隔delay。

compose函数

这个直接上代码了,是从redux那边借过来的。

/**
 * Composes single-argument functions from right to left. The rightmost
 * function can take multiple arguments as it provides the signature for
 * the resulting composite function.
 *
 * @param {...Function} funcs The functions to compose.
 * @returns {Function} A function obtained by composing the argument functions
 * from right to left. For example, compose(f, g, h) is identical to doing
 * (...args) => f(g(h(...args))).
 */exportdefaultfunctioncompose(...funcs) {
  if (funcs.length === 0) {
    returnarg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

核心代码就一行,没错,就一行。

没看到之前,我可能会通过for循环来做,上面代码用到数组方法reduce,在配合剩余参数语法和扩展运算符...来传参就可以实现函数组合的功能,非常精简。

介绍到这里就结束了,后续碰到到惊艳的函数还会继续分享。