JavaScript类是否具有与Python类的__call__等效的方法?

时间:2019-05-18 00:25:24

标签: javascript python

在Python中,您可以为一个类实现__call__(),以便调用该类的实例本身即可执行__call__()方法。

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

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

JavaScript类是否具有等效的方法?

修改

在此处包含讨论的摘要答案:

  • Python和JavaScript对象对于 true 等效项(基于原型与基于类,self,与this的相似性不够)
  • 可以使用proxy或修改原型,甚至可以使用bind来实现API
  • 通常不应该这样做:它与JS的结构以及JavaScript的正常使用相距太远,有可能在其他JS开发人员,您的未来自我中造成混淆,并可能导致特有的副作用。

3 个答案:

答案 0 :(得分:5)

唯一的方法是让构造函数显式返回一个可以调用的函数。 (在Javascript中,如果未在构造函数中显式地return,则返回新创建的实例-但这样的实例将是一个普通对象,而不是函数。)

class Example {
  constructor() {
    return function() {
      console.log('function running');
    }
  }
}

const e = new Example();
e();

但这确实很奇怪,并且不允许您引用原型上的任何属性或类似内容。最好避免使用它,或者制作一个返回函数的普通函数:

const example = () => () => console.log('function running');
const e = example();
e();

答案 1 :(得分:4)

我基本上同意@CertainPerformace的观点,这实际上不是您在普通JS代码中会做的事情。话虽如此,代理提供了很多可能性,您可以创建出与Python的__call__()接近(表面上)出乎意料的东西。

例如:

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

我真的不确定这是否是一个好主意,但这是一个有趣的实验。

答案 2 :(得分:3)

您可以完成此操作,但是方式很怪异。

JavaScript中没有__call__()__add__()__sub__()之类的东西-JavaScript不支持运算符重载。

但是,如果您真的想使对象可调用,则可以通过给函数提供其他原型来实现:

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