13

In Python, you can implement __call__() for a class such that calling an instance of the class itself executes the __call__() method.

class Example:
    def __call__(self):
        print("the __call__ method")

e = Example()
e() # "the __call__ method" 

Do JavaScript classes have an equivalent method?

Edit

A summary answer incorporating the discussion here:

  • Python and JavaScript objects are not similar enough for a true equivalent (prototype vs. class based, self, vs. this)
  • The API can be achieved however using proxy or modifying the prototype – and maybe using bind?
  • One should generally not do this: it is too far removed from the JS's structure and from the normal use of of JavaScript, potentially creating confusion among other JS devs, your future-self, and could result in idiosyncratic side-effects.
1
  • 2
    Not a huge fan of “don’t-do-this” on SO. But. As a Python dev I tried to force JS to treat ‘this’ more like ‘self’ on my early JS code, using ‘bind’ or the like. It works but I am now stuck with code that is un-JS in nature and hard to figure by a JS dev. Not something I’ll repeat. YMMV As regards to call I seem to recall a Design Pattern from the original book called Command, i.e. an object encapsulating a function. Relevant in JS? Commented May 19, 2019 at 18:22

3 Answers 3

6

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.

Sign up to request clarification or add additional context in comments.

Comments

5

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();

Comments

3

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 { ... }'

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.