Do JavaScript classes have a method equivalent to Python classes' __call__?
You can get this done, but in a rather weird way.
There isn't anything like __call__()
, __add__()
or __sub__()
in JavaScript - JavaScript does not support operator overloading.
However if you really want to make objects callable, you can do it by giving a function a different prototype:
function createCallableObject(cls, fn) {
// wrap the original function to avoid modifying it
const wrapper = (...args) => fn(...args)
// set the prototype of the wrapped function so we can call class methods
Object.setPrototypeOf(wrapper, cls.prototype)
return wrapper
}
class Example {
method() { console.log('This is an instance of class Example') }
}
function example() { console.log('This is a function') }
const obj = createCallableObject(Example, example)
obj() // 'This is a function'
obj.method() // 'This is an instance of class Example'
console.log(obj.constructor) // 'class Example { ... }'
I basically agree with @CertainPerformace that this isn't really something you would do in normal JS code. Having said that, proxies offer a lot of possibilities and you can create something that is surprisingly close to (on the surface) to Python's __call__()
.
For example:
class F extends Function{
constructor(someID, arr, n){
super()
this.id = someID
this.arr = arr
this.n = n
return new Proxy(this, {
apply(target, thisArg, argumentsList) {
return target.__call__(...argumentsList);
}
})
}
__call__(a){ // simple mult functions
return a * this.n
}
*[Symbol.iterator](){ // make it iterable for demo purposes
yield *this.arr.map(this) // call itself in a map!
}
}
let f = new F("FrankenFunction", [1, 2, 3, 4], 5)
// access instance variable
console.log("id:", f.id)
// call it
console.log("calling with 100: ", f(100))
// use the iterator
// get multiples of calling this on arr
console.log([...f])
// change the __call__ function to power instead
F.prototype.__call__ = function(a){
return a ** this.n
}
// change n to get squares:
f.n = 2
// call it again with new __call__
console.log("calling with 10:", f(10)) // 10**2
console.log([...f]) // or iterate
I really not sure if any of this is a good idea, but it's an interesting experiment.
The only way to do this would be for the constructor to explicitly return a function, which can be called. (In Javascript, if you don't explicitly return
inside a constructor, the newly created instance gets returned - but such an instance will be a plain object, not a function.)
class Example {
constructor() {
return function() {
console.log('function running');
}
}
}
const e = new Example();
e();
But this would be really weird to do, and would not allow you to reference any of the properties on the prototype, or anything like that. Better to avoid it, or to make a plain function that returns a function:
const example = () => () => console.log('function running');
const e = example();
e();