防抖

你是否在日常开发中遇到一个问题,在滚动事件中需要做个复杂计算或者实现一个按钮的防二次点击操作。

这些需求都可以通过函数防抖动来实现。尤其是第一个需求,如果在频繁的事件回调中做复杂计算,很有可能导致页面卡顿,不如将多次计算合并为一次计算,只在一个精确点做操作。

防抖和节流的作用都是防止函数多次调用。区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于wait,防抖的情况下只会调用一次,而节流的 情况会每隔一定时间(参数wait)调用函数。

我们先来看一个袖珍版的防抖理解一下防抖的实现:

  1. // func是用户传入需要防抖的函数
  2. // wait是等待时间
  3. const debounce = (func, wait = 50) => {
  4. // 缓存一个定时器id
  5. let timer = 0
  6. // 这里返回的函数是每次用户实际调用的防抖函数
  7. // 如果已经设定过定时器了就清空上一次的定时器
  8. // 开始一个新的定时器,延迟执行用户传入的方法
  9. return function(...args) {
  10. if (timer) clearTimeout(timer)
  11. timer = setTimeout(() => {
  12. func.apply(this, args)
  13. }, wait)
  14. }
  15. }
  16. // 不难看出如果用户调用该函数的间隔小于wait的情况下,上一次的时间还未到就被清除了,并不会执行函数

这是一个简单版的防抖,但是有缺陷,这个防抖只能在最后调用。一般的防抖会有immediate选项,表示是否立即调用。这两者的区别,举个栗子来说:

  • 例如在搜索引擎搜索问题的时候,我们当然是希望用户输入完最后一个字才调用查询接口,这个时候适用延迟执行的防抖函数,它总是在一连串(间隔小于wait的)函数触发之后调用。
  • 例如用户给interviewMap点star的时候,我们希望用户点第一下的时候就去调用接口,并且成功之后改变star按钮的样子,用户就可以立马得到反馈是否star成功了,这个情况适用立即执行的防抖函数,它总是在第一次调用,并且下一次调用必须与前一次调用的时间间隔大于wait才会触发。

下面我们来实现一个带有立即执行选项的防抖函数

  1. // 这个是用来获取当前时间戳的
  2. function now() {
  3. return +new Date()
  4. }
  5. /**
  6. * 防抖函数,返回函数连续调用时,空闲时间必须大于或等于 wait,func 才会执行
  7. *
  8. * @param {function} func 回调函数
  9. * @param {number} wait 表示时间窗口的间隔
  10. * @param {boolean} immediate 设置为ture时,是否立即调用函数
  11. * @return {function} 返回客户调用函数
  12. */
  13. function debounce (func, wait = 50, immediate = true) {
  14. let timer, context, args
  15. // 延迟执行函数
  16. const later = () => setTimeout(() => {
  17. // 延迟函数执行完毕,清空缓存的定时器序号
  18. timer = null
  19. // 延迟执行的情况下,函数会在延迟函数中执行
  20. // 使用到之前缓存的参数和上下文
  21. if (!immediate) {
  22. func.apply(context, args)
  23. context = args = null
  24. }
  25. }, wait)
  26. // 这里返回的函数是每次实际调用的函数
  27. return function(...params) {
  28. // 如果没有创建延迟执行函数(later),就创建一个
  29. if (!timer) {
  30. timer = later()
  31. // 如果是立即执行,调用函数
  32. // 否则缓存参数和调用上下文
  33. if (immediate) {
  34. func.apply(this, params)
  35. } else {
  36. context = this
  37. args = params
  38. }
  39. // 如果已有延迟执行函数(later),调用的时候清除原来的并重新设定一个
  40. // 这样做延迟函数会重新计时
  41. } else {
  42. clearTimeout(timer)
  43. timer = later()
  44. }
  45. }
  46. }

整体函数实现的不难,总结一下。

对于按钮防点击来说的实现:如果函数是立即执行的,就立即调用,如果函数是延迟执行的,就缓存上下文和参数,放到延迟函数中去执行。一旦我开始一个定时器,只要我定时器还在,你每次点击我都重新计时。一旦你点累了,定时器时间到,定时器重置为 null,就可以再次点击了。

对于延时执行函数来说的实现:清除定时器ID,如果是延迟调用就调用函数

节流

防抖动和节流本质是不一样的。防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行。

  1. /**
  2. * underscore 节流函数,返回函数连续调用时,func 执行频率限定为 次 / wait
  3. *
  4. * @param {function} func 回调函数
  5. * @param {number} wait 表示时间窗口的间隔
  6. * @param {object} options 如果想忽略开始函数的的调用,传入{leading: false}。
  7. * 如果想忽略结尾函数的调用,传入{trailing: false}
  8. * 两者不能共存,否则函数不能执行
  9. * @return {function} 返回客户调用函数
  10. */
  11. _.throttle = function(func, wait, options) {
  12. var context, args, result;
  13. var timeout = null;
  14. // 之前的时间戳
  15. var previous = 0;
  16. // 如果 options 没传则设为空对象
  17. if (!options) options = {};
  18. // 定时器回调函数
  19. var later = function() {
  20. // 如果设置了 leading,就将 previous 设为 0
  21. // 用于下面函数的第一个 if 判断
  22. previous = options.leading === false ? 0 : _.now();
  23. // 置空一是为了防止内存泄漏,二是为了下面的定时器判断
  24. timeout = null;
  25. result = func.apply(context, args);
  26. if (!timeout) context = args = null;
  27. };
  28. return function() {
  29. // 获得当前时间戳
  30. var now = _.now();
  31. // 首次进入前者肯定为 true
  32. // 如果需要第一次不执行函数
  33. // 就将上次时间戳设为当前的
  34. // 这样在接下来计算 remaining 的值时会大于0
  35. if (!previous && options.leading === false) previous = now;
  36. // 计算剩余时间
  37. var remaining = wait - (now - previous);
  38. context = this;
  39. args = arguments;
  40. // 如果当前调用已经大于上次调用时间 + wait
  41. // 或者用户手动调了时间
  42. // 如果设置了 trailing,只会进入这个条件
  43. // 如果没有设置 leading,那么第一次会进入这个条件
  44. // 还有一点,你可能会觉得开启了定时器那么应该不会进入这个 if 条件了
  45. // 其实还是会进入的,因为定时器的延时
  46. // 并不是准确的时间,很可能你设置了2秒
  47. // 但是他需要2.2秒才触发,这时候就会进入这个条件
  48. if (remaining <= 0 || remaining > wait) {
  49. // 如果存在定时器就清理掉否则会调用二次回调
  50. if (timeout) {
  51. clearTimeout(timeout);
  52. timeout = null;
  53. }
  54. previous = now;
  55. result = func.apply(context, args);
  56. if (!timeout) context = args = null;
  57. } else if (!timeout && options.trailing !== false) {
  58. // 判断是否设置了定时器和 trailing
  59. // 没有的话就开启一个定时器
  60. // 并且不能不能同时设置 leading 和 trailing
  61. timeout = setTimeout(later, remaining);
  62. }
  63. return result;
  64. };
  65. };

参考资料:

前端进阶之道

javascript之防抖与节流的更多相关文章

  1. JavaScript:防抖与节流

    ①防抖: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <titl ...

  2. Javascript的防抖和节流、VUE的防抖和节流

    js原生 函数防抖:将几次操作合并为一此操作进行.原理是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置.这样一来,只有最后一次操作能 ...

  3. 原生JavaScript实现函数的防抖和节流

    原生JavaScript实现函数的防抖和节流 参考:https://www.jianshu.com/p/c8b86b09daf0 想详细了解的直接戳上面链接了,讲得非常清楚.下面只给代码和我自己写的注 ...

  4. 来聊聊JavaScript中的防抖和节流

    目录 JavaScript防抖和节流 问题还原 防抖 什么是防抖 使用场景 节流 什么是节流 使用场景 JavaScript防抖和节流 问题还原 我们先来通过代码把常见的问题还原: <html& ...

  5. 彻底搞懂JavaScript的闭包、防抖跟节流

    最近出去面试了一下,收获颇多!!! 以前的我,追求实际,比较追求实用价值,然而最近面试,传说中的面试造火箭,工作拧螺丝,竟然被我遇到了.虽然很多知识点在实际工作中并不经常用到,但人家就是靠这个来筛选人 ...

  6. JavaScript 中的防抖和节流

    什么是防抖 函数防抖(debounce):当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时.如下图,持续触发 scrol ...

  7. JavaScript中函数防抖、节流

    码文不易,转载请带上本文链接,感谢~ https://www.cnblogs.com/echoyya/p/14565642.html 目录 码文不易,转载请带上本文链接,感谢~ https://www ...

  8. js高阶函数应用—函数防抖和节流

    高阶函数指的是至少满足下列两个条件之一的函数: 1. 函数可以作为参数被传递:2.函数可以作为返回值输出: javaScript中的函数显然具备高级函数的特征,这使得函数运用更灵活,作为学习js必定会 ...

  9. js防抖和节流

    今天在网上看到的,里面的内容非常多.说下我自己的理解. 所谓的防抖就是利用延时器来使你的最后一次操作执行.而节流是利用时间差的办法,每一段时间执行一次.下面是我的代码: 这段代码是右侧的小滑块跟随页面 ...

随机推荐

  1. 7年.NET面试Java的尴尬历程

    先简单介绍LZ 现如今的情况,LZ 1992年出生,2012年实习,大专学渣一枚,实习期直接被校企合作直招到公司做.NET开发,现如今在某三线城市做后端技术经理,7年开发经验(5年.Net,2年.NE ...

  2. Java-Maven(十):Maven 项目常用plugins

    本文主要总结最近一段时间使用maven时,遇到需要maven plugins的一些简单总结. 1)在Build下重新指定最终打包报名 <build> <!--最终打包的包名,如果这里 ...

  3. 大数据/NoSQL经典电子书pdf下载

    Hadoop系列 Cloudera出品的各种官方文档 入门必备 https://www.cloudera.com/documentation.html Cloudera Hadoop大数据平台实战指南 ...

  4. Dart匿名方法函数和闭包

    /* 内置方法/函数: print(); 自定义方法: 自定义方法的基本格式: 返回类型 方法名称(参数1,参数2,...){ 方法体 return 返回值; } */ void printInfo( ...

  5. jenkins结合httprunner配置实现自动化测试

    jenkins结合httprunner配置实现自动化测试 自动化测试思路: jenkins发布代码到预发布环境 –> 触发自动化测试预发布环境 à 返回测试结:如果测试通过则将代码推送到生产环境 ...

  6. 从Windows转mac遇到的一些坑

    本屌由于运气,分了一台imac来办公,由于以前一直都在用windows,现在用Mac有很多不适应的地方.下面介绍一下Mac与Windows有明显区别的地方: mac与windows鼠标滑轮方向不同. ...

  7. python链接mysql pymysql

    python链接mysql import pymysql conn = pymysql.connect(user=', database='gbt2019', charset='utf8') curs ...

  8. Cheng MeiChun团队的技术支持

    本网页为Cheng MeiChun团队的技术支持网址,如果在我们开发的游戏中遇到任何问题,欢迎联系我们! QQ:1505478990 邮箱:1505478990@qq.com

  9. vue-cli4.0 基于 antd-design-vue 二次封装发布到 npm 仓库

    1. 安装 cli npm install -g @vue/cli vue create winyh-ui 2.安装 antd-design-vue cnpm i ant-design-vue --s ...

  10. js文档系统-jsdoc-docdash

    一.参考文档 模版:https://github.com/clenemt/docdash 例子:http://clenemt.github.io/docdash/index.html jsdoc:ht ...