How to determine if a function was called with `.call` or` .apply`?
I created this function to combine the methods:
export default function bindable(fn) {
return function boundFn(...args) {
return this === undefined ? fn(...args) : fn(this, ...args);
}
}
It works with the binding operator. For example, you can define a new function like
export const flatten = bindable(arrayOfArrays => Array.prototype.concat(...arrayOfArrays));
And then use it like this:
[[1,2,3],[4,5,6]]::flatten()
Or like this:
flatten([[1,2,3],[4,5,6]])
This worked great until I realized that if I use it like in the second scenario, but import the helper method as a module, it breaks the context!
import * as Arr from './array-helper-methods';
Arr.flatten([1,2,3]); // `this` is now `Arr`
So my question is, is there a way that I can reliably determine if a function has been called using a bind statement?
source to share
This is a big and yet unsolved problem with the binding operator. But no, it only works for methods. You just have to provide two versions of your functions if you want to support both styles.
It is difficult to do this dynamically. No, you cannot tell if a function was called with ()
, as a method, was associated with call
or apply
, etc. And you really shouldn't be able to anyway.
In your case, I would choose
function bindable(fn, isContext) {
return function boundFn(...args) {
return isContext(this) ? fn(this, ...args) : fn(...args);
}
}
export default bindable(bindable, f => typeof f == "function");
Then you can use bindable(flatten, Array.isArray)
or flatten::bindable(Array.isArray)
for flatten
. For more complex cases, you can also include arity function, ie fn.length + 1 == args.length
.
source to share
A more general (but not very good performance) solution would be to check if any method is a this
function boundFn
:
export default function bindable(fn) {
return function boundFn(...args) {
return (this == null || Object.values(this).some(x => x === boundFn))
? fn(...args)
: fn(this, ...args);
}
}
source to share