1

Essentially I want to wrap (not extend) a function but the wrapper shall be callable just like the function. I can use a function (Example 1 or 2) to execute the call function.

My question is why can't I just copy the call function (Example 3)? If I do this, I get the error Function.prototype.call called on incompatible Object

function FunctionWrapper( description, functionReference ) {
    this.description = description;
    /* 1 */ this.call = function( thisArg ) { functionReference.call( thisArg ) };
    /* 2 */ this.call = thisArg => functionReference.call( thisArg );
    /* 3 */ this.call = functionReference.call;
}

function StringHolder( string ) {
    this.string = string;
}

StringHolder.prototype.log = function() {
    console.log( this.string );
};

let logger = new FunctionWrapper( "This is a logger", StringHolder.prototype.log );
logger.call( new StringHolder( "bar" ) );
Hachi
  • 3,237
  • 1
  • 21
  • 29

1 Answers1

2

The problem with #3 is that this within call is wrong. In #1 and #2, this within call is functionReference (the function passed to FunctionWrapper), but in #3 this is the FunctionWrapper instance, not functionReference. call expects this to be a function object, but your FunctionWrapper isn't a function, which is why it gives you that error message. More in How does the "this" keyword work?.

If you want logger.call( new StringHolder( "bar" )); to work, you need to do one of these:

  • Use #1, which is just fine
  • Use #2, which is just fine (I'd say slightly better than #1)
  • Use bind, e.g.:

    this.call = functionReference.call.bind(functionReference);
    
  • Remember functionReference on your object and provide your own call that uses it.

Side note: Don't forget apply, call's counterpart. :-)

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • An elaboration of what TJ writes, for clarity: Remember that `call` belongs to the `Function` prototype. So it expects that `this` refers to a function, but when you call `this.call(...)`, `this` is bound to _an object_, not a `Function`, meaning it fails to find the stuff it expects on the prototype, hence the error message. That's why you need to bind it to the functionReference, as TJ writes. Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call – oligofren Jul 03 '19 at 08:52
  • @oligofren - Of course, functions *are* objects... :-) I should have called out the reason for the specific error in the answer (so I've done that now), thanks. :-) – T.J. Crowder Jul 03 '19 at 09:12
  • 1
    okay I thought I can cheat on the context. For other methods/arguments this would work. Thank you for the explanation. And yes I won't forget apply, I just wanted to keep the example simple – Hachi Jul 03 '19 at 09:25