debounce防抖函数,throttle节流函数

所需知识点

  • 闭包
  1. 闭包的特点:
    (1) 函数内部可以引用外部的参数和变量
    (2) 参数和变量保存在内存中,不会被垃圾回收机制回收(全局变量也会长期保存在内存中)
  2. 缺点:使用不当,容易造成内存泄漏
  3. 全局变量和局部变量是否可以累加?
    (一般情况,全局变量可以累加,而局部变量不能累加,闭包除外)
  4. 很详细 https://segmentfault.com/a/1190000000652891
全局变量的累加:  (全局变量可以累加)
<script>
var a = 1;
function b(){
     a++;
     alert(a);
}
b();            //2
b();            //3
</script>


-----------------------------------------------------------------


局部变量的累加: (局部变量不能累加)
<script>
function b(){
     var a = 1;
     a++;
     alert(a);
}
b();            //2
b();            //2
</script>


-----------------------------------------------------------------


闭包中变量的累加: (闭包可以累加变量)
注意: 因为闭包常驻内存中,所以在执行完后,手动清掉
如下面: y = null; 即时清掉
<script>
function outer(){
        var x=10;
        return function(){             //函数嵌套函数
                x++;
                alert(x);
        }
}
var y = outer();      
y();                 // y函数调用一次,结果为11,相当于outer()();
y();                // y函数调用第二次,结果为12,实现了累加


y = null;       
   
!(注意) 因为y是全局变量,常驻内存,而y依赖于outer(),所以outer也常驻内存,不会被垃圾回收机制清掉

</script>   
  • 闭包在react中的使用(事件和闭包)
  1. 点击事件普通函数
  2. 点击事件闭包

点击事件:普通函数,传参
<div onClick={ (e) => this.common(e, 'anthorParams') }>点击普通函数</div>
common = (e: React.MouseEvent<HTMLDivElement>, anthor: string ): void => {
    console.log(e.target);
    console.log(anthor);
}





点击事件:闭包,传参
<div onClick={ this.closure('name') }>点击闭包函数</div> ----!!注意这里相当于closure('name')()
closure = (name: string) => {
  console.log(name, '1');          -------------------------- 注意:‘1’只会执行一次
  // 比如 let a = 1;
  // 重点:闭包外层变量a能保存在内存中,是因为let a = 1执行一次就没有再从新赋为 1
  // 这也就解释了上面console 1 的时候,为什么只执行了一次
  return (e: React.MouseEvent<HTMLDivElement>) => {  -------- e 在这里获取
    console.log(e.target);
    console.log(name, '2');         ------------------------- 注意:‘2’会多次执行
  };
}

防抖函数 debounce

特点:延时执行,如果在延时的时间内多次触发,则从新计时

延迟执行,设置一个延迟时间a,在事件A发生时,延迟a秒执行事件A,
如果A发生后,a秒内有新的同一事件再次发生,则a从新计时(即又要再延时a秒才会触发事件A)

节流函数 throttle

特点:每隔一段时间,只执行一次

在时间a内,只执行一次函数,多次触发,也只执行一次

来源于网络

竖线的疏密代表事件执行的频繁程度。可以看到,正常情况下,竖线非常密集,函数执行的很频繁。而debounce(函数防抖)则很稀疏,只有当鼠标停止移动时才会执行一次。throttle(函数节流)分布的较为均已,每过一段时间就会执行一次。





debounce函数

原理:
在闭包中设置一个变量来保存setTimeout上一次的状态,有新的输入的时候,清除定时器,再开启一个新的定时器,即重新延迟执行事件

<div onClick={ this.debounce(1000) }>点击debounce函数</div>

debounce = (delay: number) => {
  let timer: any = null; ---- 闭包中的状态保存在内存中,不会被垃圾回收机制清除,所以能保存上一次的状态
  return () => {
    if (timer) {
      window.clearTimeout(timer);  ----- 只要有新的输入,就清掉上一次的定义器,从新执行新的定时器
    }
    timer = window.setTimeout(() => {
      console.log('11111111111');
    }, delay);
  };
}

throttle函数

原理
用闭包设置一个变量开关a,a默认为true,点击后,执行闭包
先判断a的值( 决定是否执行定时器 ),
再把a变为false(目的是为了在规定的延时时间内,再次点击,不再执行定时器 ),
delay秒后,再执行定时器,a变为true,就又会执行定时器了。

<div onClick={ this.throttle(1000) }>点击throttle函数</div>

throttle = (delay: number) => {
  let isRun: boolean = true;
  return () => {
    if (!isRun) {
      return;
    }
    isRun = false;
    window.setTimeout(() => {
      console.log('111111');
      isRun = true;
    }, delay);
  };
}

利用时间戳,实现throttle函数

export const throttle = (fn,wait) => {
    var previous = null; //记录上一次运行的时间
    var timer = null;

    return function(){
        var now = +new Date();
        if ( !previous ) {
            previous = now;
        }
        if(now - previous > wait){
            clearTimeout(timer); -----  这里在时间到后默认会执行一次,如果不想执行,直接return即可
            fn();
            previous = now;   
        }else{
            clearTimeout(timer);
            timer = setTimeout(function(){
                fn();
            },wait);
        }
    }
}


let a = +new Date()
等于
let a = new Date().valueOf()   ----- 返回实例对象距离时间零点对应的毫秒数
也等于
let a = new Date().getTime() 

详细:https://juejin.im/entry/58c0379e44d9040068dc952f
精简:https://segmentfault.com/a/1190000008768202
完美://www.greatytc.com/p/f9f6b637fd6c

2019/3/26复习
throttle-Date版本:now-last>delay时,执行函数,小于时不执行,要保证第一次要执行,所以把last初始值设置为0

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <div id='d'>debounce防抖函数-点击</div>
  <div id='t'>throttle节流函数-点击  setTime版</div>
  <div id='td'>throttle节流函数-点击  Date版本</div>
  <script>
    window.onload = function() {
      // debounce防抖函数
      function debounce(t) {
        let timer;
        return () => {
          window.clearTimeout(timer);
          timer = window.setTimeout(() => {
            console.log('debounce')
          }, t)
        }
      }
      function throttle(t) {
        let isRun = true;
        return () => {
          if (!isRun) {
            return;
          }
          isRun = false;
          window.setTimeout(() => {
            console.log('throttle');
            isRun = true;
          }, t)
        }
      }
      function throttleDate(t) {
        let last = 0;         ----------------- 定义last,保存在内存中,会积累
        return () => {
          let now = + new Date();
          if ( now - last > t) { - 大于delay时间的话,就执行函数,并把last重新赋值到执行完最新的时间
            console.log('throttle-Date');
            last = + new Date();
          }
        }
      }
      //点击防抖函数
      const d = document.getElementById('d');
      d.onclick = debounce(1000);
      // 点击节流函数
      const t = document.getElementById('t');
      t.onclick = throttle(1000);
      // 点击节流函数- Date()
      const td = document.getElementById('td');
      td.onclick = throttleDate(1000);
    }
  </script>
</body>
</html>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 第二十二章:高级技巧 本章内容: 使用高阶函数 防篡改对象 Yieding Timers 22.1 高级函数 22...
    穿牛仔裤的蚊子阅读 658评论 0 1
  • 前言 最近和前端的小伙伴们,在讨论面试题的时候。谈到了函数防抖和函数节流的应用场景和原理。于是,想深入研究一下两者...
    youthcity阅读 23,623评论 5 78
  • 函数节流 还记得上篇文章中说到的图片懒加载吗?我们在文章的最后实现了一个页面滚动时按需加载图片的方式,即在触发滚动...
    柏丘君阅读 2,870评论 1 19
  • 长久以来,面向对象在 JavaScript 编程范式中占据着主导地位。不过,最近人们对函数式编程的兴趣正在增长。函...
    神刀阅读 484评论 0 0
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,141评论 1 32