• JS高级 之 防抖debounce - throttle节流


    目录

    一、防抖 debounce

    1. 概念

    2. 应用场景

    3. 使用 underscore 实现防抖

    01 - 代码

    02 - 效果

    4. 实现

    01 - 基本实现

            代码

            效果

    02 - 优化 => this 和 参数绑定

            代码

            效果 

    03 - 优化 => 取消功能

            代码

            效果

    04 - 优化 => 立即执行功能

            代码

            效果

    05 - 优化 => 获取返回值

            代码

    06 - 最终版本

    二、节流 throttle 

    1. 概念

    2. 应用场景

    3. 使用 underscore 实现节流

    01 - 代码

    02 - 效果

    4. 实现

    01 - 基本实现

            代码

            效果

    02 - 优化 => this 和 参数的绑定

            代码

            效果

    03 - 优化 => 立即执行功能

            代码

            效果

    04 - 优化 => 获取返回值

            代码

    05 - 最终版本

    5. 第二种方法实现节流


    一、防抖 debounce

    1. 概念

    • 当事件触发时,相应的函数并不会立即触发,而是会等待一定的时间
    • 当事件密集触发时,函数的触发会被频繁的推迟,并把上一次的给取消掉
    • 只有等待了一段时间也没有事件触发,才会真正的执行响应函数

    2. 应用场景

    • 输入框中频繁的输入内容,搜索或者提交信息
    • 频繁的点击按钮,触发某个事件
    • 监听浏览器滚动事件,完成某些特定操作
    • 用户缩放浏览器的resize事件

    3. 使用 underscore 实现防抖

    01 - 代码

    1. <input type="text" />
    2. <script src="https://cdn.jsdelivr.net/npm/underscore@1.13.4/underscore-umd-min.js">script>
    3. <script>
    4. // 2.获取input元素
    5. const inputEl = document.querySelector('input');
    6. // 3.防抖处理代码
    7. let counter = 1;
    8. inputEl.oninput = _.debounce(function () {
    9. console.log(`发送网络请求${counter++}:`, this.value);
    10. }, 1000);
    11. script>

    02 - 效果

    4. 实现

    01 - 基本实现

            代码

    1. <input type="text" />
    2. <script>
    3. function starDebounce(fn, delay) {
    4. // 1.用于记录上一次事件触发的timer
    5. let timer = null;
    6. // 2. 返回新的函数
    7. return function () {
    8. // 3. 如果有再次触发(更多次触发)事件, 那么取消上一次的事件
    9. if (timer) clearTimeout(timer);
    10. // 4. 绑定当前事件
    11. timer = setTimeout(() => {
    12. // 5. 延迟后执行
    13. fn();
    14. // 6. 执行后把当前定时器删除
    15. timer = null;
    16. }, delay);
    17. };
    18. }
    19. script>
    20. <script>
    21. const inputDom = document.querySelector('input');
    22. let counter = 1;
    23. inputDom.oninput = starDebounce(function () {
    24. console.log(`发送网络请求${counter++}`);
    25. }, 1000);
    26. script>

            效果

    02 - 优化 => this 和 参数绑定

            代码

    1. <input type="text" />
    2. <script>
    3. function starDebounce(fn, delay) {
    4. let timer = null;
    5. // 1. 返回新的函数,此时这个函数中的this为绑定的dom对象 => 相当于 inputDom的oninput指向这个函数
    6. // 2. 接受参数
    7. return function (...args) {
    8. if (timer) clearTimeout(timer);
    9. timer = setTimeout(() => {
    10. // 3. 绑定this,同时把参数传递给fn
    11. fn.apply(this, args);
    12. timer = null;
    13. }, delay);
    14. };
    15. }
    16. script>
    17. <script>
    18. const inputDom = document.querySelector('input');
    19. let counter = 1;
    20. inputDom.oninput = starDebounce(function (e) {
    21. console.log(`发送网络请求 :${counter++} => ${this.value} `, e);
    22. }, 1000);
    23. script>

            效果 

    03 - 优化 => 取消功能

            代码

    1. <input type="text" />
    2. <button>取消button>
    3. <script>
    4. function starDebounce(fn, delay) {
    5. let timer = null;
    6. // 1. 返回新的函数,此时这个函数中的this为绑定的dom对象 => 相当于 inputDom的oninput指向这个函数
    7. // 2. 接受参数
    8. const _debounce = function (...args) {
    9. if (timer) clearTimeout(timer);
    10. timer = setTimeout(() => {
    11. // 3. 绑定this,同时把参数传递给fn
    12. fn.apply(this, args);
    13. timer = null;
    14. }, delay);
    15. };
    16. // 3. 因为函数也是一个对象,所以
    17. _debounce.cancle = function () {
    18. if (timer) clearTimeout(timer);
    19. timer = null;
    20. };
    21. // 4. 返回函数
    22. return _debounce;
    23. }
    24. script>
    25. <script>
    26. const inputDom = document.querySelector('input');
    27. let counter = 1;
    28. const debounceFn = starDebounce(function (e) {
    29. console.log(`发送网络请求 :${counter++} => ${this.value} `, e);
    30. }, 1000);
    31. // 执行
    32. inputDom.oninput = debounceFn;
    33. // 取消
    34. const btnDom = document.querySelector('button');
    35. btnDom.onclick = debounceFn.cancle;
    36. script>

            效果

    04 - 优化 => 立即执行功能

            代码

    1. <input type="text" />
    2. <button>取消button>
    3. <script>
    4. function starDebounce(fn, delay, immediate = false) {
    5. let timer = null;
    6. // 1. 是否是第一次执行
    7. let isInvoke = true;
    8. const _debounce = function (...args) {
    9. if (timer) clearTimeout(timer);
    10. // 2. 第一次操作不需要延迟
    11. if (immediate && isInvoke) {
    12. fn.apply(this, args);
    13. timer = null;
    14. isInvoke = false;
    15. return;
    16. }
    17. timer = setTimeout(() => {
    18. fn.apply(this, args);
    19. timer = null;
    20. // 3. 执行完后恢复
    21. isInvoke = true;
    22. }, delay);
    23. };
    24. _debounce.cancle = function () {
    25. if (timer) clearTimeout(timer);
    26. timer = null;
    27. // 3. 执行完后恢复
    28. isInvoke = true;
    29. };
    30. return _debounce;
    31. }
    32. script>
    33. <script>
    34. const inputDom = document.querySelector('input');
    35. let counter = 1;
    36. const debounceFn = starDebounce(
    37. function (e) {
    38. console.log(`发送网络请求 :${counter++} => ${this.value} `, e);
    39. },
    40. 1000,
    41. true
    42. );
    43. // 执行
    44. inputDom.oninput = debounceFn;
    45. // 取消
    46. const btnDom = document.querySelector('button');
    47. btnDom.onclick = debounceFn.cancle;
    48. script>

            效果

    05 - 优化 => 获取返回值

            代码

    1. <input type="text" />
    2. <button>取消button>
    3. <script>
    4. function starDebounce(fn, delay, immediate = false) {
    5. let timer = null;
    6. let isInvoke = true;
    7. const _debounce = function (...args) {
    8. // 1. 因为有延迟,使用promise
    9. return new Promise((resolve, reject) => {
    10. try {
    11. let res = null;
    12. if (timer) clearTimeout(timer);
    13. // 2. 第一次操作不需要延迟
    14. if (immediate && isInvoke) {
    15. // 2. 接受函数的返回值
    16. res = fn.apply(this, args);
    17. // 3. 传递出去
    18. resolve(res);
    19. timer = null;
    20. isInvoke = false;
    21. return;
    22. }
    23. timer = setTimeout(() => {
    24. res = fn.apply(this, args);
    25. // 3. 传递出去
    26. resolve(res);
    27. timer = null;
    28. // 3. 执行完后恢复
    29. isInvoke = true;
    30. }, delay);
    31. } catch (error) {
    32. reject(error);
    33. }
    34. });
    35. };
    36. _debounce.cancle = function () {
    37. if (timer) clearTimeout(timer);
    38. timer = null;
    39. // 3. 执行完后恢复
    40. isInvoke = true;
    41. };
    42. return _debounce;
    43. }
    44. script>
    45. <script>
    46. const debounceFn = starDebounce(function (name, text, age) {
    47. console.log(name, text, age);
    48. return '执行完了';
    49. }, 1000);
    50. // 4. 传递参数并接受返回值
    51. debounceFn('coder', 'star', 18).then((res) => {
    52. console.log(res);
    53. });
    54. // 取消
    55. const btnDom = document.querySelector('button');
    56. btnDom.onclick = debounceFn.cancle;
    57. script>

    06 - 最终版本

    1. function starDebounce(fn, delay, immediate = false) {
    2. let timer = null;
    3. let isInvoke = true;
    4. const _debounce = function (...args) {
    5. return new Promise((resolve, reject) => {
    6. try {
    7. let res = null;
    8. if (timer) clearTimeout(timer);
    9. if (immediate && isInvoke) {
    10. res = fn.apply(this, args);
    11. resolve(res);
    12. timer = null;
    13. isInvoke = false;
    14. return;
    15. }
    16. timer = setTimeout(() => {
    17. res = fn.apply(this, args);
    18. resolve(res);
    19. timer = null;
    20. isInvoke = true;
    21. }, delay);
    22. } catch (error) {
    23. reject(error);
    24. }
    25. });
    26. };
    27. _debounce.cancle = function () {
    28. if (timer) clearTimeout(timer);
    29. timer = null;
    30. isInvoke = true;
    31. };
    32. return _debounce;
    33. }

    二、节流 throttle 

    1. 概念

    • 当事件触发时,会执行这个事件的响应函数
    • 如果这个事件会被频繁触发,那么节流函数会按照一定的频率来执行函数
    • 不管在这个中间有多少次触发这个事件,执行函数的频繁总是固定的

    2. 应用场景

    • 监听页面的滚动事件
    • 鼠标移动事件
    • 用户频繁点击按钮操作
    • 轮播图的按钮滚动

    3. 使用 underscore 实现节流

    01 - 代码

    1. <input type="text" />
    2. <script src="https://cdn.jsdelivr.net/npm/underscore@1.13.4/underscore-umd-min.js">script>
    3. <script>
    4. // 2.获取input元素
    5. const inputEl = document.querySelector('input');
    6. // 3.节流处理代码
    7. let counter = 1;
    8. inputEl.oninput = _.throttle(function () {
    9. console.log(`发送网络请求${counter++}:`, this.value);
    10. }, 1000);
    11. script>

    02 - 效果

    4. 实现

    01 - 基本实现

            代码

    1. <input type="text" />
    2. <script>
    3. /**
    4. * fn : 传入的函数
    5. * interval : 间隔的请求时间
    6. * 公式 : 等待时间 = 间隔时间 - ( 当前时间 - 开始时间 ) => interval - ( nowTime - startTime )
    7. *
    8. * 等待时间 <= 0 即可执行函数
    9. */
    10. function starThrottle(fn, interval) {
    11. // 1. 获取开始时间,赋初始值为0
    12. let startTime = 0;
    13. function throttle() {
    14. // 2. 获取当前时间
    15. const nowTime = new Date().getTime();
    16. // 3. 计算等待时间
    17. const waitTime = interval - (nowTime - startTime);
    18. // 4. 判断是否执行函数,第一次会默认执行
    19. if (waitTime <= 0) {
    20. fn();
    21. // 5.一旦执行完后,把当前时间赋值给开始时间
    22. startTime = nowTime;
    23. }
    24. }
    25. return throttle;
    26. }
    27. script>
    28. <script>
    29. // 2.获取input元素
    30. const inputEl = document.querySelector('input');
    31. // 3.节流处理代码
    32. let counter = 1;
    33. inputEl.oninput = starThrottle(function () {
    34. console.log(`发送网络请求${counter++}:`, this.value);
    35. }, 2000);
    36. script>

            效果

    02 - 优化 => this 和 参数的绑定

            代码

    1. <input type="text" />
    2. <script>
    3. function starThrottle(fn, interval) {
    4. let startTime = 0;
    5. // 节流函数接受参数
    6. function throttle(...args) {
    7. const nowTime = new Date().getTime();
    8. const waitTime = interval - (nowTime - startTime);
    9. if (waitTime <= 0) {
    10. // 相当于 inputEl 直接调用该函数,所以this指向 inputEl
    11. // 把参数传给函数,即可在回调函数中拿到
    12. fn.apply(this, args);
    13. startTime = nowTime;
    14. }
    15. }
    16. return throttle;
    17. }
    18. script>
    19. <script>
    20. // 2.获取input元素
    21. const inputEl = document.querySelector('input');
    22. // 3.节流处理代码
    23. let counter = 1;
    24. inputEl.oninput = starThrottle(function (e) {
    25. // 使用
    26. console.log(`发送网络请求${counter++}:`, this.value, e);
    27. }, 2000);
    28. script>

            效果

    03 - 优化 => 立即执行功能

            代码

    1. <input type="text" />
    2. <script>
    3. /**
    4. * fn : 传入的函数
    5. * interval : 间隔时间
    6. * immediate : 第一次是否执行,默认是马上执行的
    7. */
    8. function starThrottle(fn, interval, immediate = true) {
    9. let startTime = 0;
    10. function throttle(...args) {
    11. const nowTime = new Date().getTime();
    12. // 如果immediate为false,且 开始时间为0时
    13. // 把当前时间赋值给开始时间,这样使得第一次不会执行
    14. if (!immediate && startTime === 0) {
    15. startTime = nowTime;
    16. }
    17. const waitTime = interval - (nowTime - startTime);
    18. if (waitTime <= 0) {
    19. fn.apply(this, args);
    20. startTime = nowTime;
    21. }
    22. }
    23. return throttle;
    24. }
    25. script>
    26. <script>
    27. // 2.获取input元素
    28. const inputEl = document.querySelector('input');
    29. // 3.节流处理代码
    30. let counter = 1;
    31. inputEl.oninput = starThrottle(
    32. function (e) {
    33. // 使用
    34. console.log(`发送网络请求${counter++}:`, this.value, e);
    35. },
    36. 1000,
    37. false
    38. );
    39. script>

            效果

    04 - 优化 => 获取返回值

            代码

    1. <script>
    2. // 3.节流处理代码
    3. let counter = 1;
    4. const throttleFn = starThrottle(function (...e) {
    5. // 使用
    6. console.log(`发送网络请求${counter++}:`, e);
    7. return '无敌 🦖 战神';
    8. }, 1000);
    9. throttleFn('inside', 'args').then((res) => {
    10. console.log('返回值:', res);
    11. });
    12. script>

    05 - 最终版本

    1. function starThrottle(fn, interval, immediate = true) {
    2. let startTime = 0;
    3. function throttle(...args) {
    4. return new Promise((resolve, reject) => {
    5. try {
    6. const nowTime = new Date().getTime();
    7. if (!immediate && startTime === 0) {
    8. startTime = nowTime;
    9. }
    10. const waitTime = interval - (nowTime - startTime);
    11. if (waitTime <= 0) {
    12. //
    13. const res = fn.apply(this, args);
    14. resolve(res);
    15. startTime = nowTime;
    16. }
    17. } catch (error) {
    18. reject(error);
    19. }
    20. });
    21. }
    22. return throttle;
    23. }

    5. 第二种方法实现节流

    1. // 直接最终版本
  • 相关阅读:
    10、SpringBoot_测试用例
    Spring AOP 简介
    【深入浅出 Yarn 架构与实现】2-2 Yarn 基础库 - 底层通信库 RPC
    人工智能对我们的生活带来的影响
    C#:实现渗透建堆算法(附完整源码)
    dicom镜像反转90度
    MySQLBackup备份数据库
    微服务和Spring Cloud Alibaba介绍
    Unity InputField宽度自适应内容
    Windows系统使用MinGW编译二维码库Zbar
  • 原文地址:https://blog.csdn.net/a15297701931/article/details/126603269