current position:Home>Learn debounce from the underscore Library

Learn debounce from the underscore Library

2021-08-27 00:15:52 agil

This is my participation 8 The fourth of the yuegengwen challenge 22 God , Check out the activity details :8 Yuegengwen challenge

Above, Introduction to anti shake This paper briefly introduces the use scenario of anti shake and an example of a simple point implementation , Support general needs , There are some restrictions on its use , for example

  • To execute a function this Point to the problem
  • Parameter acquisition problem of executing function
  • Cannot execute immediately

Is there a feature rich anti shake ?

Yes ! Here it comes This article will solve a high configuration version of anti shake underscore Anti shake function in Library

Let's first introduce the example background of this chapter : There is one div, stay div Move up trigger callback function listenMoveOn, A simple version of anti shake is used here , As shown in the following code

<style>    div {        background-color: #666;        height: 300px;   } </style><div></div><script>    let dom = document.getElementsByTagName("div")[0];    dom.onmousemove = debounce(listenMoveOn, 200); ​    function listenMoveOn() {     //  Listening callbacks do something     }    /**     *     * @param {function} fn -  Callback function      * @param {number} delay -  Waiting time      */    function debounce(fn, delay) {        var timer = null;     return function (...args) {            clearTimeout(timer);            timer = setTimeout(fn, delay);       };   } </script>
 Copy code 

this Point to the problem

here listenMoveOn Printed in this Point to window, The reason lies in listenMoveOn It's through setTimeout Called , Remember whose method this is ?window.setTimeout, Is it window!!! therefore listenMoveOn Medium this Point to window

because onmousemove The callback to listen is debounce The function returned by the function body , So in this function body this It points to this dom Elemental

function listenMoveOn() {
    // this -> window
}
function debounce(fn, delay) {
    ...
    return function () {
        // this -> dom
    };
}
 Copy code 

Now that things are clear , So change listenMoveOn In the body of the function this Pointing should not be difficult ? Use the following apply

function listenMoveOn() {
    // this -> dom 
}
function debounce(fn, delay) {
    ...
    return function () {
        // this -> dom
        ...
        timer = setTimeout(() => {
            fn.apply(this)          //  Change here listenMoveOn Of this Point to 
        }, delay);      
    };
}
 Copy code 

The question of reference transmission

This problem can be related to this Point together solve , But I still want to say more , Because the event that I monitor the mouse movement is debounce The function returned by the function body ,js Events usually return an event object , I can only debounce In order to get , In event callback listenMoveOn You can't get it in , The solution is as follows

function listenMoveOn(...args) {
    let [event] = args
    // args You can get the event object returned by event listening 
    // this -> dom 
}
function debounce(fn, delay) {
    ...
    return function (...args) {
        // this -> dom
        ...
        timer = setTimeout(() => {
            fn.apply(this, args)    //  Change here listenMoveOn Of this Pass in the parameters obtained by the listening object while pointing to , Of course use arguments It's fine too 
        }, delay);      
    };
}
 Copy code 

Execute now

This implementation is a little complicated , about underscore Medium debounce I also changed the source code a little , The principle of function realization is the same , See the code comments for details , Basically every line has a comment !!( Because after this implementation, it will be implemented as a whole , So the notes are more careful )

function listenMoveOn(...args) {
  //  Listening callbacks do something 
  // this -> dom
  // args -> [event]
}
​
/** * * @param {function} fn -  Callback function  * @param {number} delay -  Waiting time  * @param {boolean} immediate -  Whether to execute immediately  */
function debounce(fn, delay, immediate = false) {
  /**   *   * @param timer -  Timer    * @param timestamp -  current time    * @param context -  Save context using    * @param param -  Save event parameter usage    */
  let timer, timestamp, context, param;
​
  const later = function() {
    let last = +new Date() - timestamp  //  Time interval from last operation ms
    if(last < delay && last >= 0) {
        timer = setTimeout(later, delay - last) //  Reset timer 
    } else {  //  Get into  else  Explain the settings delay It's time 
        timer = null   //  Clear the saved variables in the closure , And if there's a follow-up , You can immediately call callNow Change to true, Call the event callback function 
        if (!immediate) {
            fn.apply(context, param)   //  If immediate = false You need to call the event handler ; When immediate = true  when , Because... Was called immediately , So you don't need to call... Later , In this code   The fifth to the last line   Execute immediate call 
            context = param = null  //  Clear the saved variables in the closure 
        }
    }
  }
  
  // return This function is the function of mouse event monitoring , As long as the mouse is div The last move will trigger here 
  return function (...args) {
    context = this  //  Save variables into closures , It is convenient to get the value when calling later 
    param = args
    timestamp = +new Date()
      
    let callNow = immediate && !timer   //  When  immediate  Set up true, also timer When it has not been assigned , That means I just came in , Can be called immediately , The following sentence will be re assigned timer
    if (!timer) timer = setTimeout(later, delay)    //  When entering the setting for the first time timer
    if (callNow) {                      
        fn.apply(context, args)         //  The immediate call is here 
        context = param = null          //  Clear the saved variables in the closure 
    }
  };
}
 Copy code 

underscore Medium debounce

Finally, you can enjoy underscore Medium debounce Source code


// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
  _.debounce = function(func, wait, immediate) {
    var timeout, args, context, timestamp, result;
​
    var later = function() {
      var last = _.now() - timestamp;
​
      if (last < wait && last >= 0) {
        timeout = setTimeout(later, wait - last);
      } else {
        timeout = null;
        if (!immediate) {
          result = func.apply(context, args);
          if (!timeout) context = args = null;
        }
      }
    };
​
    return function() {
      context = this;
      args = arguments;
      timestamp = _.now();
      var callNow = immediate && !timeout;
      if (!timeout) timeout = setTimeout(later, wait);
      if (callNow) {
        result = func.apply(context, args);
        context = args = null;
      }
​
      return result;
    };
  };
 Copy code 

This article source address

Summary of this chapter

If there is anything wrong , Welcome to correct

copyright notice
author[agil ],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2021/08/20210827001547775H.html

Random recommended