Search code examples
javascriptthisapplyjscontext

pass javascript function as paremeter with THIS in the implementation - bind and apply don't work


I'm trying to create a library that allows the user to pass a custom function. This custom function has to be able to use the properties of the parent object, which holds references determined at another time.

This is a simplification of the actual library (which is a kind of test/verification library that has default functions, but also allows users to pass a custom test/check).

console.log("hello?");

const object = {
  parameter: undefined,
  execute: undefined,
  executeNow: function() {
    this.execute.apply(this);
  }
};

function executeMeLater(func) {
  object.parameter = "hello!";
  object.execute = func;
  return object;
};

executeMeLater(() => {
  console.log(this.parameter);
}).executeNow();

The problem is that apply fails to apply the context of const object. Instead, this still refers to the original context. As a result, this.parameter is undefined because this doesn't refer to "object". This is more obvious in my real-world code.

Does anyone know how I can make this work? Bear in mind that this is supposed to be a library. Another file imports executeMeLater and then calls it with the custom function (including references to this) as a parameter, i.e. executeMeLater(() => {/* stuff*/}).


Solution

  • Arrow functions don't have their own bindings to this (or arguments or super). This means they cannot be used as methods, which is what I'm trying to do in the example.

    The solution is pretty obvious: pass a method instead of an arrow function.

    console.log("hello?");
    
    const object = {
      parameter: undefined,
      execute: undefined,
      executeNow: function() {
        this.execute.apply(this);
      }
    };
    
    function executeMeLater(func) {
      object.parameter = "hello!";
      object.execute = func;
      return object;
    };
    
    executeMeLater(function(){ console.log(this.parameter); }).executeNow();
    

    Note the last part:

    executeMeLater(() => { console.log(this.parameter); }).executeNow();
    

    has become

    executeMeLater(function(){ console.log(this.parameter); }).executeNow();
    

    We can also just declare the function outright:

    function methodWithBindingToThis() {
       console.log(this.parameter);
    }
        
    executeMeLater(methodWithBindingToThis).executeNow();
    

    Or we can use an expression:

    const methodWithBindingToThis = function() {
        console.log(this.parameter);
    }
    
    executeMeLater(methodWithBindingToThis).executeNow();