[概念] js的函数节流和throttle和debounce详解

js的函数节流和throttle和debounce详解:
同样是实现了一个功能,可能有的效率高,有的效率低,这种现象在高耗能的执行过程中区分就比较明显。
本章节一个比较常用的提高性能的方式,通常叫做"函数节流",下面就通过代码实例对此做一下介绍。
一.函数节流是什么:
在实际编码中,mousemove和resize是使用非常频繁的事件类型(当然还有其他类似事件类型),这样的事件有一个共同的特点,就是在一个普通的操作中,就有可能会在极短的时间内多次执行事件处理函数,会极大的损耗性能,比如resize事件,每次执行通常都会对dom元素进行重新计算,这是非常损耗性能的,何况是频繁的执行,所以在不影响效果的情况下尽可能的减少事件处理函数的执行次数是非常重要的。
二.函数节流的方式:
1.throttle方式:
此单词英文就有"节流"的意思。
更通俗的说就是调节函数执行的频率,控制函数连续两次执行的时间间隔。
下面一个代码:

[JavaScript] 纯文本查看 复制代码运行代码
1
2
3
4
5
6
7
8
9
var timer=null;
$(window).on('resize',function(){
  if(timer){
    clearTimeout(timer)
  }
  timer=setTimeout(function(){
    console.log("蚂蚁部落");
  },400);
});


代码实现了函数节流效果,当调整浏览器窗口大小的时候,"蚂蚁部落"就不会被输出,当调整停止后的400毫秒后才会输出。
上面的方式的的确确实现了函数节流效果,但是感觉有点太狠了,通常我们需要的不是当调整的时候不会输出,而是输出的频率低一点,实现这样的功能,后面会分享一段代码。
2.debounce方式:
此方式和throttle类似,但是它的关注点是函数调用的时间间隔,throttle的关注点是函数执行的时间间隔。
debounce方式对于控制长按键盘暴力输入这种现象非常有效。
三.实例代码:
如果相对函数节流彻底理解掌握,最好完整的分析一段实现此功能的源码。

[JavaScript] 纯文本查看 复制代码运行代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
var throttle = function (fn,delay, immediate, debounce) { 
  var curr = +new Date(),
  last_call = 0,
  last_exec = 0,
  timer = null,
  diff,
  context,
  args,
  exec = function () {
    last_exec = curr;
    fn.apply(context, args);
  };
  return function () {
    curr= +new Date();
    context = this,
    args = arguments,
    diff = curr - (debounce ? last_call : last_exec) - delay;
    clearTimeout(timer);
    if(debounce){
      if(!immediate){
        timer=setTimeout(exec, delay);
      }
      else if(diff >= 0){
        exec();
      }
    }
    else{
      if(diff >= 0){
        exec();
      }
      else if(!immediate){
        timer = setTimeout(exec, -diff);
      }
    }
    last_call = curr;
  }
};


代码虽然将函数对象引用赋值给名为throttle的变量,其实它实现throttle和debounce两种方式,只要调整一下参数即可,下面就对它的实现过程做一下注释。
代码注释:
1.var throttle = function (fn,delay, immediate, debounce) {},此函数能够返回一个可以作为事件处理函数,具有四个参数,fn是一个函数对象,被节流的就是它的执行,delay规定延迟执行的时间,单位是毫秒,immediate是一个布尔值,如果是true,那么相关函数就会立即执行,否则延迟执行,debounce是个布尔值,如果是true,那么就是采用debounce方式。
2.var curr = +new Date(),获取当前时间,是一个时间戳。
3.last_call = 0,声明一个变量并初始化为0,它用来存储上一次试图调用函数的时间。
4.last_exec = 0,声明一个变量并初始化为0,它用来存储上一次函数函数执行的时间。
5.timer = null,声明一个变量,并初始化null,它会存储定时器函数的标识。
6.diff,用来存储时间差。
7.context,用来存储一个上下文对象。
8.args,用来存储函数的参数。
9.exec = function () {
  last_exec = curr;
  fn.apply(context, args);
};声明一个函数,此函数可以获取函数执行的事件,并且执行通过参数传递过来的fn函数。
10.return function () {},返回一个函数,被返回的这个函数就是不断被调用的事件处理函数。
11.curr= +new Date(),获取当前时间对象的时间戳。
12.context = this,将context赋值为this。
13.args = arguments,将参数对象集合赋值给变量args。
14.diff = curr - (debounce ? last_call : last_exec) - delay,这一个很有技巧,可以获取当前时间与延迟时间和上一次被调用的时间或者执行时间的时间差。
15. clearTimeout(timer),停止定时器函数的执行。
16.if(debounce){    if(!immediate){
    timer=setTimeout(exec, delay);
  } 
  else if(diff >= 0){
    exec();
  }
}
如果采用debounce方式,并且采用延迟方式,那么函数会延迟指定的时间再去执行,如果没有采用延迟方式,并且时间差大于等于0(说明延迟时间已到),就执行函数。
17.else{
  if(diff >= 0){
    exec();
  } 
  else if(!immediate){
    timer = setTimeout(exec, -diff);
  }
}
如果采用的是throttle方式,那么就会判断diff 是否大于0,如果大于0就执行函数,第一次执行的时候肯定会大于0,也就是说throttle方式在第一次执行的时候不会有延迟效果。如果不大于零,且采用延迟模式的话,就会延迟执行。
也许看了上面的介绍,对两种方式感觉比较混乱,那么可以参阅函数节流throttle和debounce代码演示一章节,这样就可以比较直观的观察两者的作用和相互间的区别了。

原文地址:https://www.cnblogs.com/smght/p/4446606.html