• JavaScript函数防抖与截流


    一、在前端开发当中,有些交互事件,会频繁触发。这样会导致我们的页面渲染性能,如果频繁触发接口调用的话,会直接导致服务器性能的浪费。

         例如:键盘事件 keyup作为测试

    1. <ul>
    2. <li>
    3. 未做处理:<input type="text" id="pt">
    4. <div>函数被调用:<span id="count">0</span></div>
    5. </li>
    6. </ul>
    1. const count = document.getElementById('count');
    2. const pt = document.getElementById('ipt');
    3. let init = 0
    4. pt.onkeyup = function() {
    5. count.innerText = ++init
    6. }

    运行效果

           每次输入都会触发事件的执行。如果我们用这样的方式去检测:当前用户输入的用户名是否可用?如此高频率的触发不仅是极大的浪费,而且用户还没有输入完成就检测,对用户的提示也不好。应该等用户输入完了,我们再去触发函数,下面我们优化一下:

    1. <ul>
    2. <li>
    3. 未做处理:<input type="text" id="pt">
    4. <div>函数被调用:<span id="count">0</span></div>
    5. </li>
    6. <li>
    7. 防抖处理:<input type="text" id="pt2">
    8. <div>函数被调用:<span id="count2">0</span></div>
    9. </li>
    10. </ul>
    1. const Count2 = document.getElementById('count2');
    2. const pt2 = document.getElementById('pt2');
    3. // 设置一个默认值 500ms
    4. const debounce = (fn, wait = 500) => {
    5. let time = null
    6. return function(arguments) {
    7. const _this = this, args = arguments
    8. clearTimeout(time)
    9. time = setTimeout(() => {
    10. fn.apply(_this, [args])
    11. }, wait)
    12. }}
    13. let init2 = pt2.onkeyup = debounce(function() {
    14. Count2.innerText = ++init2

    运行效果:

           可以看到,加了防抖函数之后,当我们在频繁输入的时候,函数并没有执行, 只有在函数指定的间隔内(500ms)不再输入了,才会执行函数。如果在时间间隔之内继续输入,会触发函数重新计数。

           函数防抖:在事件触发后的n秒之后,再去执行真正需要执行的函数,如果在这n秒之内事件又被触发,则重新开始计时

           也就是说,如果用户在间隔时间内一直触发函数,那么这个防抖函数内部的真正需要执行的函数将永远无法执行。

           那有没有什么好点的办法,让用户在输入过程中,既能触发真实需要的函数,又能达到优化的效果?

         答案是肯定的,那就是:

          函数截流:规定好一个单位时间,触发函数一次。如果在这个单位时间内触发多次函数的话,只有一次是可被执行的。想执行多次的话,只能等到下一个周期里

    1. <li>
    2.    截流处理:<input type="text" id="pt3">
    3.    <div>函数被调用:<span id="count3">0</span></div>
    4.    <div>当前时间(分/秒):<span id="time"></span></div>
    5. </li>

     

    1. const count3 = document.getElementById('count3');
    2. const pt3 = document.getElementById('pt3');
    3. const time = document.getElementById('time');
    4. const throttle = (fn, hold = 1000) => {
    5. let last, deferTimer
    6. return function(arguments) {
    7. const _this = this, args = arguments
    8. let now = +new Date()
    9. if(last && now < last + hold) {
    10. clearTimeout(deferTimer)
    11. deferTimer = setTimeout(function () {
    12. last = now
    13. fn.call(_this, args)
    14. }, hold)
    15. } else {
    16. last = now
    17. fn.call(_this, args)
    18. }
    19. }}
    20. let init3 = 0
    21. const Ipt = throttle(function() {
    22. let time = new Date().getMinutes() + ':' + new Date().getSeconds()
    23. time.innerText = time
    24. count3.innerText = ++init3
    25. }, 1000) // 初始化一下
    26. oIpt3.onkeyup = function() {
    27. Ipt()
    28. }

    二、应用场景:防抖和截流都是用来防止高频率的js代码的执行

       1、防抖:防抖本质上就是以最后的操作为标准

    例如:此时此刻我们都在排队等公交,司机说必须等到坐满才会发车,这时候

    的参照标准就是最后一个人上车,公交车好比我们的js代码,最后一个人就充当我们的

    执行条件。例如:

    1. let setTimer;
    2. let shake = function() {
    3.   clearTimeout(setTimer);
    4.   setTimer = setTimeout(() => {
    5.     console.log("这里是实际的业务代码");
    6.   }, 0);
    7. };
    8. let interTimer = setInterval(() => {
    9.   shake();
    10. }, 0);
    11. let timer = setTimeout(() => {
    12.   clearInterval(interTimer);
    13.   clearTimeout(timer);
    14.   timer = null;
    15.   interTimer = null;
    16. }, 2000);

    执行以上代码,控制台会在 2s 后打出日志,2s 之内的操作都被清空,以最后一次的操作为准。

            如果监听滚动事件,假设两秒以内用户在不断的平凡的触发onScroll事件,只有用户暂停滚动后,才会去执行响应的操作,代码如下

    1. // 函数防抖
    2. var timer = false;
    3. document.getElementById("xxxx").onscroll = function(){
    4. clearTimeout(timer); // 清除未执行的代码,重置回初始化状态
    5. timer = setTimeout(function(){
    6. console.log("函数防抖");
    7. }, 300);
    8. };

         2、截流:

             (1)定时器实现截流

    1. let isAllow = true;
    2. function shake() {
    3.   let fun = function() {
    4.     if (!isAllow) return;
    5.     isAllow = false;
    6.     let timer = setTimeout(() => {
    7.       console.log("这里是实际的业务代码");
    8.       clearTimeout(timer);
    9.       timer = null;
    10.       isAllow = true;
    11.     }, 1000);
    12.   };
    13.   fun();
    14. }
    15. let interTimer = setInterval(() => {
    16.   shake();
    17. }, 0);

       执行以上会看到控制台每隔 1s 后打印出结果,1s 内不会执行打印日志

              (2)闭包实现函数截流:

    1. // fn是我们需要包装的事件回调, interval是时间间隔的阈值
    2. function fun(fn, interval) {
    3.     let last = 0; // last为上一次触发回调的时间
    4.     // 将throttle处理结果当作函数返回
    5.     return function() {
    6.       let context = this; // 保留调用时的this上下文
    7.       let args = arguments; // 保留调用时传入的参数
    8.       let now = +new Date(); // 记录本次触发回调的时间
    9.       // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
    10.       if (now - last >= interval) {
    11.         // 如果时间间隔大于我们设定的时间间隔阈值,则执行回调
    12.         last = now;
    13.         fn.apply(context, args);
    14.       }
    15.     };
    16.   }
    17.   // 用fun来包装scroll的回调
    18.   const better_scroll = fun(() => console.log("触发了滚动事件"), 1000);
    19.   setInterval(() => better_scroll(), 0);
    20.  

  • 相关阅读:
    Pyhotn: Mac安装selenium没有chromedriver-114以上及chromedriver无法挪到/usr/bin目录下的问题
    `ExecutorService` 接口
    详解自监督发展趋势! 何恺明连获三年CVPR最高引用的秘诀是?
    使用 git cherry-pick 命令可以将指定的提交从一个分支移动到另一个分支
    【Linux操作系统】——安装VMware
    如何修改mtp模式在电脑上显示的存储容量大小?
    Transformer【第五章】
    线性代数学习笔记9-4:复习——相似矩阵、对角化、对称矩阵
    首款内置电源的迷你主机,不到千元的办公神器 | 零刻EQ13评测报告
    【RuoYi-Vue-Plus】扩展笔记 03 - 实现简单的 EasyExcel 自定义导入监听器
  • 原文地址:https://blog.csdn.net/m0_37911706/article/details/125544533