Recently in the review, want to test their learning degree through feynman learning method, so bold in this article, if you give directions, very lucky!

All code in this article can be run directly from the console.

A, buffer

Buffering, this is a very common concept, anything to slow down or weaken the process of change can be called buffering. There is also a need for buffering, known as debounce and throttle, in web interactions — making high-frequency events react more slowly is a cliche, but one that cannot be ignored.

This is already implemented in Lodash.

  • Debounce: A small delay is set immediately after an event is triggered. If the event is triggered again during the delay, the previous delay is destroyed and the delay starts again; Otherwise, start executing the handler. In short, keep resetting the timer. For example: the elevator door will automatically close if no one enters within a few seconds.
  • Throttle: To execute handlers at intervals during successive events. Example: Autosave articles every few minutes during editing.

Second, the Debounce

1. Debounce basically implemented

  • Need: mouse moves on the page, when movement stops 2 seconds, prompt “mouse moved”.
  • Implementation: according to the previous definition, set a new timer when the mouse moves:
let timer = null;
function setTimer(){
    timer && clearTimeout(timer);
    timer = setTimeout(function () {
    	console.log("Mouse moved");
    }, 2000)}window.onmousemove = setTimer;
Copy the code

2. The packaging

  • Requirement: Because the above code is used frequently, it needs to be packaged into a convenient utility function, taking the event handler and wait time as parameters.
  • Implementation: Arguments are not necessarily passed exactly as parameters, so be defensive
/** * debounce V1.0 *@param {function} Fn event handler, which defaults to an empty function *@param {number} Delay Indicates the delay time, in milliseconds *@return {function} ResetTimer returns a function that can reset the timer */
function debounce(fn=function(){}, delay=0){
    let timer = null;
    function resetTimer() {
        if(timer) {clearTimeout(timer)}
        // Ensure that the delay parameter is a natural number
        const delayTime = isNaN(parseInt(delay)) ? 
            0 : Math.abs(parseInt(delay));
        timer = setTimeout(fn, delayTime);
    }
    return resetTimer;
}

function onMove() { console.log("Mouse moved")}window.onmousemove = debounce(onMove, 2000);
Copy the code

3. Execution environment: this

You often need to access this object in event handlers. The debounce function above is used on window.onmousemove, so this in onMove must refer to window. Now let’s use debounce on another object and see if the reference to this changes:

/ /... Debounce function V1.0
// Suppose there is an element with ID menu
const oBtn = document.getElementById("menu"); 
function onBtnHover() {console.log(this)};
oBtn.onmousemove = debounce(onBtnHover, 1000);
// Result: window object
Copy the code

OnBtnHover (‘ this’, ‘oBtn’);

setTimeout(fn, delayTime); / / equivalent to
function callback(){
    fn();
}
setTimeout(callback, delayTime);
Copy the code

Here fn is not bound to any object, so it belongs to the Window object, so this refers to the window.

4. Solve this direction problem

ES5: When a function is called, it automatically gets two special variables: this and arguments. When a function is called in the global environment, this represents the window object; When a function is called as a method of some other object, this represents that object.

In short: in ES5 functions, this is the object in which the function is executed.

ES6: This is the object where the function definition resides.

In the code below, debounce binds the event handler resetTimer to obtn. onmousemove. By the nature of this in ES5, when this function is run, its execution environment must be the caller:

function debounce(fn=function(){}, delay=0){
    function resetTimer() {
       console.log("ResetTimer execution environment:".this);
    }
    return resetTimer;
}
const oBtn = document.getElementById("menu");
function onBtnHover() {console.log(this)};
oBtn.onmousemove = debounce(onBtnHover, 1000);
// Result: The execution environment is oBtn
Copy the code

Therefore, you should save this in the resetTimer function, and then modify this in setTimeout using the call and apply functions. The debounce function can be modified as follows:

/** * debounce V2.0 *@param {function} Fn event handler, which defaults to an empty function *@param {number} Delay Indicates the delay time, in milliseconds *@return {function} ResetTimer returns a function that can reset the timer */
function debounce(fn=function(){}, delay=0){
    let timer = null;
    function resetTimer() {
        const context = this;  /**** Modify: Save the execution environment ****/ 
        if(timer) {clearTimeout(timer)}
        const delayTime = isNaN(parseInt(delay)) ? 
            0 : Math.abs(parseInt(delay));
        timer = setTimeout(
            function(){fn.apply(context)},  /**** Modified: Application execution environment ****/ 
            delayTime
        );
    }
    return resetTimer;
}

const oBtn = document.getElementById("menu");
function onBtnHover() {console.log("Mouse moved, execution environment is:".this)};
oBtn.onmousemove = debounce(onBtnHover, 1000);
// Result: The execution environment is oBtn
Copy the code

5. Parameter transfer

The above code ensures that the this pointer is correct, but any attempt to pass an onBtnHover parameter will affect the this pointer.

  • Requirement: The onBtnHover function prints the string passed for it
  • Implementation: call onBtnHover with an anonymous function and pass the reference
/ /... Debounce function V2.0
const oBtn = document.getElementById("menu");
function onBtnHover(text) {console.log(text, this)};
oBtn.onmousemove = debounce(
    function(){onBtnHover("Mouse moved, execution environment is:")}, 
    1000
);
// Result: The execution environment is Window
Copy the code
  • whyTo pass an argument, we call onBtnHover with an anonymous function, soapply(context)Only this of the anonymous function is modified, while this of onBtnHover remains unchanged.
  • Proof: Print this inside the anonymous function
/ /... Debounce function V2.0
const oBtn = document.getElementById("menu");
function onBtnHover(text) {console.log(text, this)};
oBtn.onmousemove = debounce(
    function(){
        console.log("Anonymous function execution environment:".this); / modify * * * * * * /
        onBtnHover("Mouse moved, execution environment is:");
    }, 
    1000
);
// Anonymous function execution environment: oBtn
// When the mouse moves, the execution environment is window
Copy the code
  • Solution 1: Modify onBtnHover’s this in an anonymous function using call and apply
/ /... Debounce function V2.0
const oBtn = document.getElementById("menu");
function onBtnHover(text) {console.log(text, this)};
oBtn.onmousemove = debounce(
    function(){
        console.log("Anonymous function execution environment:".this); 
        onBtnHover.call(this."Mouse moved, execution environment is:"); / modify * * * * * * /
    }, 
    1000
);
// Anonymous function execution environment: oBtn
// When the mouse moves, the execution environment is: oBtn
Copy the code

OnBtnHover. Apply (this, [” mouse moved, execution environment is: “])

The call() method does the same thing as the apply() method; the only difference is how the parameters are received. The former requires listing the parameters, while the latter requires putting the parameters in an array.

  • Solution 2: Simply define onBtnHover as a method of oBtn
/ /... Debounce function V2.0
const oBtn = document.getElementById("menu");
oBtn.onBtnHover = function(text) {console.log(text, this)};  / modify * * * * * * /
oBtn.onmousemove = debounce(
    function(){
        console.log("Anonymous function execution environment:".this);
        oBtn.onBtnHover("Mouse moved, execution environment is:"); / modify * * * * * * /
    }, 
    1000
);
// Anonymous function execution environment: the element pointed to by oBtn
// When the mouse moves, the execution environment is: the element oBtn points to
Copy the code

6. Debounce

Debounce is used when binding event handlers, which bind events to a function that continuously resets the timer. Using the debounce function V2.0 above requires passing it the real event handler and the delay time. If you need to pass parameters to an event handler, you control the this point in both ways.

Third, Throttle

The only difference between throttling and anti-shock is the control, so we can use Debounce’s approach.

1. Throttle is basically implemented

  • Requirement: Print “Mouse on the move” every 1 second during mouse movement.
  • Implementation: the mouse just moved will set a timer, at the same time set a mark, indicating that during the time period if the mouse moves again, then do nothing, after the end of the time to execute the processing function, and clear the mark. This way, when you move the mouse again, a new timer and marker will be set…
/** * throttle V1.0 *@param {function} Fn event handler, which defaults to an empty function *@param {number} Delay Indicates the delay time, in milliseconds *@return {function} SetNewTimer returns a function that can set a timer by time interval */
function throttle(fn = function(){}, delay = 0){
    let isTiming = false; // Whether the timer is being timed
    let timer = null;
    function setNewTimer(){
        if(isTiming) {return};
        
        // Start setting timers and markers
        isTiming = true; // Set the flag to prevent the next trigger
        const delayTime = isNaN(parseInt(delay)) ? 
            0 : Math.abs(parseInt(delay));
        let context = this;
        if(timer){clearTimeout(timer)}; // Clear the existing timer
        timer = setTimeout(function(){
            fn.apply(context);
            isTiming = false;
        }, delayTime);
    }
    return setNewTimer;
}

function onMove(){console.log( "The mouse is moving" )};
window.onmousemove = throttle(onMove, 1000);
Copy the code

2. Pass to participate in this pointing

  • Requirement: Print “Mouse moving” every second when the mouse moves over the specified element and make sure this points to the element.
  • Implementation: Use the two methods mentioned earlier
  • Option 1: Modify this of onBtnHover using call and apply in an anonymous function and pass the parameter
// throttle V1.0
const oBtn = document.getElementById("menu");
function onBtnHover(text){console.log( text, this )};
oBtn.onmousemove = throttle(
    function(){
        console.log("Anonymous function execution environment:".this);
        onBtnHover.call(this."Mouse moving, execution environment:")},1000);
Copy the code
  • Option 2: Define onBtnHover directly as a method of oBtn
// throttle V1.0
const oBtn = document.getElementById("menu");
oBtn.onBtnHover = function(text){console.log( text, this )};
oBtn.onmousemove = throttle(
    function(){
        console.log("Anonymous function execution environment:".this);
        oBtn.onBtnHover("Mouse moving, execution environment:")},1000);
Copy the code

Four,

Now that debounce and Throttle have been implemented, we can extend their capabilities by adding more parameters to them, such as another parameter indicating whether they need to be executed immediately, and so on.