js - The use and details of anti-shake and throttling functions

insert image description here

1. What is anti-shake

The event response function is executed after a period of time. If it is called again within this period, the execution time will be recalculated. You must wait for the specified time before executing this function;

2. Application scenarios

1. High-frequency event monitoring callbacks, such as onscroll, onresize, oninput, touchmove, etc.;

2. User name, mobile phone number, email input verification input box auto-completion event, search box input search (data is requested only after the user completes the last input);

3. After the browser window size is changed, wait for the adjustment to be completed, and execute the function in resize;

3. Implementation principle

In general, the principle of function throttling and function anti-shake is very simple. SetTimeout is cleverly used to store the functions to be executed, so that clearTimeout can be easily used to clear the functions to be executed at the right time. and using advanced functions formed using closures;

See the simple one below 案例:

Monitor the mouse movement event in the green container, if it keeps moving, do nothing, and print after stopping for 500 milliseconds防抖触发了

insert image description here
code show as below:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .item {
    
    
        box-sizing: border-box;
        width: 300px;
        height: 300px;
        padding: 20px;
        margin: 10px;
        border: 8px solid red;
        background-color: yellowgreen;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="item" id="item">
      </div>
    </div>
    <script>
      let item = document.getElementById("item");
      item.addEventListener("mousemove", debounce(foo));
      // 基本的防抖函数
      function debounce(foo, delay=500) {
    
    
        let timer = null;
        return function () {
    
    
          if (timer) {
    
    
            clearTimeout(timer); // 触发了相同事件,取消当前计时,重新开始计时
          }
          timer = setTimeout(foo, delay); // 第一次执行,开始一个计时器
        };
      }
       // 执行函数
       function foo() {
    
    
         console.log("防抖触发了");
      }
    </script>
  </body>
</html>

Anti-shake function 基本实现:

   function debounce(foo, delay = 500) {
    
    
        let timer = null;
        return function () {
    
    
          if (timer) {
    
    
            clearTimeout(timer); // 触发了相同事件,取消当前计时,重新开始计时
          }
          timer = setTimeout(foo, delay); // 第一次执行,开始一个计时器
        };
      }

The above will implement a basic anti-shake function, a few lines of seemingly simple code, there will be the following details:

1. The first question: why is the closure used (that is to say why the timer is defined outside)

First review what is a closure:

 	 function fun() {
    
    
        var str = 0;
        function sum() {
    
     //此处就形成了闭包
          return ++str; //这里使用了外部函数内的变量,此变量会一直被保存下来
        }
        return sum;
      }
      var count = fun();
      console.log(count()); // 1
      console.log(count()); // 2
      console.log(count()); // 3

Closure concept :

闭包(closure)Refers to a function that has access to a variable in another function's scope. The simple understanding is that a scope can access local variables inside another function.

This is due to the unique scope chain search of js. The global variable can be directly read upwards inside the function, and it will be searched up level by level until it is found. (This process is irreversible)

Once the closure is used and the variable defined by the external function is accessed, the variable will be kept forever (excessive use will cause memory leak);

And the use of closures can also return internally defined variables for global use;

After understanding the closure, return to the above question, why use the closure, in fact, is why the timer id of timer is defined outside;

because:

If timerthe definition of is placed in return functionthe function, if (timer)this condition will not be true all the time. This will start a lot of timers. Every time this function is executed, a new setTimeout timer will be added. Then so many timers will be executed almost at the same time. To the effect of anti-shake, but also increase the running load of js;

We put timerthe definition outside, so that only one timer will be started at the same time, and the previous timer will be cleared if it comes in multiple times, and a new timer will be restarted, and the new timer id has been saved by the outside, so it will not be at the same timertime Multiple setTimeouts are generated; the anti-shake effect is achieved;

At this point, the introduction of why to use closures is complete; because of the use of timers in the anti-shake function, a new problem will arise, the pointing problem of this;

2. The second question: the pointing problem of this in the anti-shake function:

See below:

insert image description here
It can be found by printing that the first this points to the Window, and the second this points to the item node element.

As for why this of the third timer also points to window:

because :

The timer method of JS is defined under the window. If you don’t use the arrow function and actively change the point of this, the
point of this in the callback function of setInterval and setTimeout is Window;

If the context is involved in calling the anti-shake function, then the pointing of this in the timer must be considered. It is necessary to use apply to change the direction of this;

The previous basic anti-shake function can be optimized to the following code:

完整的防抖函数

      // 防抖
      function debounce(foo, delay = 500) {
    
    
        let timer = null;
        return function () {
    
    
          let _this = this; // 此处保留this
         
          if (timer) {
    
    
            clearTimeout(timer); 
          }
          timer = setTimeout(function(){
    
    
            foo.apply(_this) // 改变this指向
          }, delay); 
        };
      }

4. Throttle function

The details of the throttling function are the same as the anti-shake, so I won’t go into details here, mainly because the way of implementation is a little different;

concept:

节流(throttel)No matter how frequently the event is triggered, the target function will only be executed once at the set interval. To put it simply: every unit of time, it is only executed once.

Application scenario: when the scroll bar scrolls, the function processing can appropriately reduce the number of responses by throttling;

Basic implementation: basically can meet most needs

   function throttle(foo, delay = 500) {
    
    
        let timer = null;
        return function () {
    
    
          if (timer) {
    
    
            return false; //定时器还在,等冷却时间
          }
          timer = setTimeout(function(){
    
    
            foo();
            clearTimeout(timer); //函数执行完后清除定时器,表示冷却完成
            timer = null;        //此处一点要清空 
          }, delay); 
        };
      }

Implementation with apply:

      function throttle(foo, delay = 500) {
    
    
        let timer = null;
        return function () {
    
    
          let _this = this;
          if (timer) {
    
    
            return false; //定时器还在,等冷却时间
          }
          timer = setTimeout(function(){
    
    
            foo.apply(_this);
            clearTimeout(timer); //函数执行完后清除定时器,表示冷却完成
            timer = null;        //此处一点要清空 
          }, delay); 
        };
      }

Although there are many versions of these anti-shake throttling functions on the Internet, it is important to know why they are written in this way. It is enough to realize the function, and there is no need to make it so complicated;

Guess you like

Origin blog.csdn.net/qq_43886365/article/details/131844131