atkayla
atkayla

Reputation: 8839

When writing a debounce, what is the point of fn.apply(context, args)?

function debounce(fn, delay) {
  let timer = null;
  // this1
  return function() {
    let context = this, args = arguments;
    // context = this2
    clearTimeout(timer);
    timer = setTimeout(function() {
      // this3
      fn.apply(context, args);
    }, delay);
  }
}

var debouncedFn = debounce(function() {
  console.log('hi');
}, 50);

debouncedFn();

Are this1, this2, and this3 not the same? What is the intention of fn.apply()? Why do we choose to fn.apply(this2) and not just fn() or fn.apply(this1) or fn.apply(this3) in this case?

Upvotes: 2

Views: 946

Answers (2)

Vipin Kumar
Vipin Kumar

Reputation: 6546

Let`s add some names to functions

function debounce(fn, delay) {
  let timer = null;
  return function functionX() {
    let context = this, args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function functionY() {
      fn.apply(context, args);
    }, delay);
  }
}

var debouncedFn = debounce(function functionZ() {
  console.log('hi');
}, 50);

debouncedFn();

Why .apply()?: Here debouncedFn holds the reference of functionX, that means if you execute debouncedFn, functionX will be executed. Now when you pass n number of parameters to debouncedFn, those can be retrieved in functionX via a reserved keyword arguments. Ultimately, you want your arguments to be supplied to fn, so for passing those arguments to fn we have to use apply(). For more info, please follow this link.

this1, this2, this3: debounce is always executed globally, that is why it's context is window, functionX() can be attached to an object, that is why context of functionX() will be object in which you are putting debouncedFn() and context of setTimeout is again window because of global execution. See following example for better understanding of context.

function debounce(fn, delay) {
  let timer = null;
  console.log('this1', this.name);
  return function() {
    let context = this,
      args = arguments;
    console.log('this2', this.name);
    clearTimeout(timer);
    timer = setTimeout(function() {
       console.log('this3', this.name);
      fn.apply(context, args);
    }, delay);
  }
}


var person = {
  name: 'John',
  age: 23,
  getName: debounce(function() {
    console.log('hi');
  }, 50)
}

person.getName();
person.getName();

Upvotes: 3

ibrahim mahrir
ibrahim mahrir

Reputation: 31682

  1. To preserve the context (the value of this) of the debounced function.
  2. To handle unknown number of arguments.

1.

The function may get attached to an object, so when calling the function the context should be that object. The function that get attached to such object is not fn, it's the anonymous function returned by debounce. So when calling fn we have to call it with the same context as that anonymous function using either call or apply.

2.

The function that actually get called is the anonymous function, that will then call fn after some time. Since we don't know how many arguments does fn have, we just call it with all the arguments that the anonymous function get called with using the arguments object and aplly (call won't be used here as it takes separate argument not grouped-in-array-like arguments).

The thisN question:

No, they're not the same.

this1 is going to be window as debounce is not attached to any object.

this2 could be window or could be anything else depending on how you call debounce:

var f = debounde(someFn, 100);   // this2 is window
var obj = {};
obj.f = debounce(someFn, 100);   // this2 is obj

this3 is also window, due to this.

Upvotes: 3

Related Questions