函数防抖

什么是防抖?
某些时候某些操作是高频触发的,但是其实触发一次就好了,比如我们短时间内多次请求接口,那么我们不应该每次点击都去请求接口,应该只做一次就好。再比如说监听输入框的输入,不应该每次都去触发监听,应该是用户完成一段输入后在进行触发。

所以,我们的思路就是:1、对于在事件被触发 N 秒后再执行的回调(延迟执行);2、如果在这 N 秒内再次触发事件,那么就重新开始计时
防抖和节流 - 图1

  1. var oBox = document.getElementsByClassName("box")[0];
  2. var t = null; // 保存定时器
  3. oBox.onclick = function () {
  4. // 每次都先清除定时器
  5. clearTimeout(t);
  6. // 然后立马赋值,当 1 秒频繁触发事件的时候,第 9 行是不会执行的!
  7. t = setTimeout(function () {
  8. console.log(1);
  9. }, 1000);
  10. };

这样就实现了一个简单的防抖操作,可是我觉得还是不够完美,因为当我们第一次点击oBox的时候也会延迟执行,那我们就可以抽离一个防抖函数,实现自定义是否第一次就延迟执行。

  1. var oBox = document.getElementsByClassName("box")[0];
  2. oBox.onmouseover = debounce(handleEvent, 1000, true);
  3. function handleEvent() {
  4. console.log(1);
  5. }
  6. function debounce(fn, time, triggleNow) {
  7. var t = null;
  8. var res;
  9. return function () {
  10. var _self = this;
  11. var arg = arguments;
  12. if (t) {
  13. // 每次都先清除定时器
  14. clearTimeout(t);
  15. }
  16. // 如果 triggleNow 为 true,就表示第一次的时候不需要延迟执行
  17. if (triggleNow) {
  18. // 如果 t 是 null 的话,exec 就是 true
  19. var exec = !t;
  20. // t 立马被赋值为定时器,1 秒内再次触发事件的时候 !t 就为 false 了
  21. t = setTimeout(function () {
  22. // 1 秒后再把 t 赋值为 null,下次 !t 又变成 true 了
  23. t = null;
  24. }, time);
  25. // 这里比 setTimeout 先执行
  26. if (exec) {
  27. res = fn.apply(_self, arguments);
  28. }
  29. } else {
  30. // 这里和我们最开始写的片段是一样的!
  31. t = setTimeout(function () {
  32. res = fn.apply(_self, arguments);
  33. }, time);
  34. }
  35. return res;
  36. };
  37. }

函数节流

函数节流和函数防抖基本类似,唯一的区别就是:防抖函数在一段时间内频繁触发事件只会执行一次,节流函数在一段时间内频繁触发事件会定时执行一次。
防抖和节流 - 图2

  1. var oInput = document.getElementsByClassName("search")[0];
  2. oInput.oninput = throttle(handleEvent, 1000);
  3. function handleEvent() {
  4. console.log("success");
  5. }
  6. function throttle(fn, delay) {
  7. var t = null;
  8. var begin = new Date().getTime(); // 调用函数的时间
  9. return function () {
  10. var _self = this;
  11. var args = arguments;
  12. var cur = new Date().getTime(); // 触发触发的时间
  13. clearTimeout(t); // 先清除定时器
  14. // 如果 当前时间-调用函数的时间 >= 设置的延迟时间
  15. // 就表示时间到了,可以立即执行
  16. if (cur - begin >= delay) {
  17. fn.apply(_self, args);
  18. // 将调用函数的时间重新赋值
  19. begin = cur;
  20. } else {
  21. // 否则就是时间还没到,只能延迟执行
  22. // 然后立即赋值定时器
  23. t = setTimeout(function () {
  24. fn.apply(_self, args);
  25. }, delay);
  26. }
  27. };
  28. }

或者也可以用一个变量来控制:

  1. function thorttle(fn, wait) {
  2. let timer;
  3. return function () {
  4. let _this = this;
  5. let args = arguments;
  6. // 如果 timer 为定时器的 id ,是不会进入 if 内的
  7. if(!timer) {
  8. timer = setTimeout(function(){
  9. // 延迟之后 timer 变为 null
  10. timer = null;
  11. fn.apply(_this, args)
  12. }, wait)
  13. }
  14. }
  15. }