I want to call a function with a custom thisArg
.
That seems trivial, I just have to call call
:
func.call(thisArg, arg1, arg2, arg3);
But wait! func.call
might not be Function.prototype.call
.
So I thought about using
Function.prototype.call.call(func, thisArg, arg1, arg2, arg3);
But wait! Function.prototype.call.call
might not be Function.prototype.call
.
So, assuming Function.prototype.call
is the native one, but considering arbitrary non-internal properties might have been added to it, does ECMAScript provide a safe way in to do the following?
func.[[Call]](thisArg, argumentsList)
Approach: Write one function inside another function. Make a call to the inner function in the return statement of the outer function. Call it fun(a)(b) where a is parameter to outer and b is to the inner function.
Answer: B) call by reference. Explanation: In the call by reference, it will just copy the address of the variable to access it, so it will reduce the memory in accessing it.
That's the power (and risk) of duck typing: if typeof func.call === 'function'
, then you ought to treat it as if it were a normal, callable function. It's up to the provider of func
to make sure their call
property matches the public signature. I actually use this in a few place, since JS doesn't provide a way to overload the ()
operator and provide a classic functor.
If you really need to avoid using func.call
, I would go with func()
and require func
to take thisArg
as the first argument. Since func()
doesn't delegate to call
(i.e., f(g, h)
doesn't desugar to f.call(t, g, h)
) and you can use variables on the left side of parens, it will give you predictable results.
You could also cache a reference to Function.prototype.call
when your library is loaded, in case it gets replaced later, and use that to invoke functions later. This is a pattern used by lodash/underscore to grab native array methods, but doesn't provide any actual guarantee you'll be getting the original native call method. It can get pretty close and isn't horribly ugly:
const call = Function.prototype.call;
export default function invokeFunctor(fn, thisArg, ...args) {
return call.call(fn, thisArg, ...args);
}
// Later...
function func(a, b) {
console.log(this, a, b);
}
invokeFunctor(func, {}, 1, 2);
This is a fundamental problem in any language with polymorphism. At some point, you have to trust the object or library to behave according to its contract. As with any other case, trust but verify:
if (typeof duck.call === 'function') {
func.call(thisArg, ...args);
}
With type checking, you can do some error handling as well:
try {
func.call(thisArg, ...args);
} catch (e) {
if (e instanceof TypeError) {
// probably not actually a function
} else {
throw e;
}
}
If you can sacrifice thisArg
(or force it to be an actual argument), then you can type-check and invoke with parens:
if (func instanceof Function) {
func(...args);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With