需求

在日常的前端开发中,一般会遇到一些会被频繁触发的事件,比如:window.resize,window.scroll,mousedown,mousemovekeydown,keyup,或者输入框等。这些函数或者是事件一般会被非常频繁的触发。

方案

为了解决这个问题一般会有两种解决方案:防抖(debounce)和节流(throttle)

防抖

原理:假定时间间隔是n秒。那么就是在函数第一次被调用时延时n秒后执行。在这n秒期间之内,再次触发,则以新的事件为准

第一版实现

  1. function deBounce(func, wait) {
  2. var timeFlag;
  3. return function () {
  4. clearInterval(timeFlag);
  5. timeFlag = setTimeout(func, wait)
  6. }
  7. }

第二版实现

第一版实现中会存在this指向不正确的问题

  1. function deBounce(func, wait) {
  2. var timeFlag;
  3. return function () {
  4. var context = this;
  5. clearInterval(timeFlag);
  6. timeFlag = setTimeout(function () {
  7. func.apply(context)
  8. }, wait)
  9. }
  10. }
  11. 或者使用bind
  12. function deBounce(func, wait) {
  13. var timeFlag;
  14. return function () {
  15. clearInterval(timeFlag);
  16. timeFlag = setTimeout(func.bind(this), wait)
  17. }
  18. }

第三版实现

第二版中的实现存在传参的问题

  1. function deBounce(func, wait) {
  2. var timeFlag;
  3. return function () {
  4. var context = this;
  5. var args = arguments
  6. clearInterval(timeFlag);
  7. timeFlag = setTimeout(function () {
  8. func.apply(context, args)
  9. }, wait)
  10. }
  11. }
  12. 或者bind
  13. function deBounce(func, wait) {
  14. var timeFlag;
  15. return function (...args) {
  16. clearInterval(timeFlag);
  17. timeFlag = setTimeout(func.bind(this, ...args), wait)
  18. }
  19. }
  20. 或者es6
  21. const deBounce = (func, wait) => {
  22. let timeFlag;
  23. return function (...args) {
  24. clearInterval(timeFlag);
  25. timeFlag = setTimeout(func.bind(this, ...args), wait)
  26. }
  27. };

第四版实现

有时会有需要立即执行的需求。

  1. function deBounce(func, wait, immediate) {
  2. var timeFlag;
  3. return function () {
  4. var context = this;
  5. var args = arguments;
  6. timeFlag && clearInterval(timeFlag);
  7. if (immediate) {
  8. var callNow = !timeFlag;
  9. timeFlag = setTimeout(function () {
  10. timeFlag = null
  11. }, wait);
  12. if (callNow) func.apply(context, args)
  13. } else {
  14. timeFlag = setTimeout(function () {
  15. func.apply(context, args)
  16. }, wait)
  17. }
  18. }
  19. }
  20. // 或者
  21. const deBounce = (func, wait, immediate) => {
  22. let timeFlag;
  23. return function (...args) {
  24. clearInterval(timeFlag);
  25. if (immediate) {
  26. const oldFlag = !timeFlag;
  27. timeFlag = setTimeout(() => {
  28. timeFlag = null
  29. }, wait);
  30. oldFlag && func.apply(this, args)
  31. } else {
  32. timeFlag = setTimeout(func.bind(this, ...args), wait)
  33. }
  34. }
  35. };

第五版

第四版的函数仍然存在部分问题。就是当函数立即执行时。可能需要函数的返回值,所以修改后有下面第五版

  1. function deBounce(func, wait, immediate) {
  2. var timeFlag;
  3. var deBounced = function () {
  4. var context = this;
  5. var args = arguments;
  6. var result;
  7. timeFlag && clearInterval(timeFlag);
  8. if (immediate) {
  9. var callNow = !timeFlag;
  10. timeFlag = setTimeout(function () {
  11. timeFlag = null
  12. }, wait);
  13. if (callNow) result = func.apply(context, args)
  14. } else {
  15. timeFlag = setTimeout(function () {
  16. func.apply(context, args)
  17. }, wait)
  18. }
  19. return result
  20. };
  21. deBounced.cancel = function () {
  22. clearTimeout(timeout);
  23. timeout = null;
  24. };
  25. return deBounced;
  26. }
  27. // es6
  28. const deBounce = (func, wait, immediate) => {
  29. let timeFlag;
  30. const deBounced = (...args) => {
  31. clearInterval(timeFlag);
  32. if (immediate) {
  33. const oldFlag = !timeFlag;
  34. timeFlag = setTimeout(() => {
  35. timeFlag = null
  36. }, wait);
  37. oldFlag && func.apply(this, args)
  38. } else {
  39. timeFlag = setTimeout(func.bind(this, ...args), wait)
  40. }
  41. };
  42. deBounced.cancel = () => {
  43. clearTimeout(timeFlag);
  44. timeFlag = null;
  45. };
  46. return deBounced
  47. };