Home > Back-end >  Can you explain why debounce does this binding?
Can you explain why debounce does this binding?

Time:06-29

function debounce(fn, delay) {
    var timer
    return function () {
      var context = this
      var args = arguments
      clearTimeout(timer)
      timer = setTimeout(function () {
        fn.apply(context, args)
      }, delay)
    }
  }
  1. I'm curious about the purpose of context = this in this code. I don't understand the code very well.

  2. Secondly, fn.apply (context, args) This part is also not well understood. Is there a special reason to bind this? You want to use the debounce function as an util function throughout the project.

export function debounce(fn, delay) {
  var timer;
  return function () {
    var args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function () {
      fn.apply(this, args);
    }, delay);
  };
}
  1. I want to know the difference between the last chord and the first chord

CodePudding user response:

  1. var context = this

The reason why you would put this into a different variable in this code is simply that function(){} has a different this value based on where it gets called. In this case it's called from setTimeout as a callback, which means that this would be whatever it is inside of setTimeout instead of what it was when the debounce inner function was called

You could get around this pretty easily in modern JavaScript using arrow functions, which have a lexical this - this is based on where the function was created instead of where it is called.

This would be the equivalent code to the initial version with the correct this binding.

function debounce(fn, delay) {
    var timer
    return function () {
      var args = arguments
      clearTimeout(timer)
      timer = setTimeout(() => {
        fn.apply(this, args)
      }, delay)
    }
  }
  1. fn.apply(context, args)

function#apply allows you to run a function with both a specific this value applied to it as well as passing in multiple arguments in an ergonomic way. Before we had rest syntax, function#apply was the only approach to this, now you can actually just use fn(...args) in modern javascript (assuming you don't have to explicitly bind the this value of the function). Just keep in mind that this is an incredibly confusing concept for nearly everyone.

The reason why you would bind context in general in the function as defined, is just so that debounce is more generic and more capable of being called in different circumstances. For example, in this case, we can use this to increment a counter based on the element that the function was called on.

In practice, you wouldn't want the same debounced function put on both, you'd want to have one function and then debounce it twice, otherwise you could end up "canceling" a click on one by clicking on the other, but it's a good example of how this can make it more functional.

function debounce(fn, delay) {
  var timer
  return function() {
    var args = arguments
    clearTimeout(timer)
    timer = setTimeout(() => {
      fn.apply(this, args)
    }, delay)
  }
}


const debounced = debounce(function() {
  this.dataset.numClicks = (Number.parseInt(this.dataset.numClicks || 0))   1;
  this.innerText = `Clicked ${this.dataset.numClicks} Times!`
  console.log(this.innerText)
}, 500);
document.querySelectorAll('button').forEach(el => el.addEventListener('click', debounced));
div {
  height: 100%;
  width: 100%;
  color: black;
  background: pink;
}
<button>Click me!</button>
<button>Click me!</button>

CodePudding user response:

Pretty easy to see what the difference is when you test it out.

function debounce1(fn, delay) {
  var timer
  return function() {
    var context = this
    var args = arguments
    clearTimeout(timer)
    timer = setTimeout(function() {
      fn.apply(context, args)
    }, delay)
  }
}

function debounce2(fn, delay) {
  var timer;
  return function() {
    var args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function() {
      fn.apply(this, args);
    }, delay);
  };
}



var button = document.querySelector('button');
button.addEventListener('click', debounce1(function(){console.log("1", this);}, 500));
button.addEventListener('click', debounce2(function(){console.log("2", this);}, 500));
<button>Click</button>

You maintain the context of what triggered the function vs using the window object in the timeout.

  • Related