假设你有一个班级
class MyClass {
world() {
console.log("hello world");
}
}
我可以运行类似于以下的方法:
var hello = new MyClass();
hello.world();
# outputs: hello world
有没有办法处理对象的直接函数调用?例如:
hello();
返回:TypeError: hello is not a function
。
我可以将此调用设为默认功能吗?例如,类似于PHP's invoke function ...
答案 0 :(得分:3)
我们只能在JavaScript中创建可调用的东西,如果该东西是某个对象,在某些时候委托给Function.prototype
。因此,我们的课程需要来自extend Function
s extend
的班级extend
或Function
。我们还需要能够从类对象中访问实例变量(以便调用invoke()
),因此需要将其绑定到自身。此绑定只能在构造函数中发生。
由于我们的课程将继承Function
,因此我们需要先致电super
才能使用this
。但是,Function
构造函数实际上需要一个我们不会拥有的代码字符串,因为我们希望以后能够设置invoke
。所以我们需要extend Function
在一个不同的类中,它将是我们类的父类,它将完成设置我们的虚函数的原型的工作(我们需要它才能够调用返回的对象)。将所有这些结合在一起,我们得到:
class ExtensibleFunction extends Function {
constructor(f) {
// our link to Function is what makes this callable,
// however, we want to be able to access the methods from our class
// so we need to set the prototype to our class's prototype.
return Object.setPrototypeOf(f, new.target.prototype);
}
}
class MyClass extends ExtensibleFunction {
constructor() {
// we build an ExtensibleFunction which accesses
// the late-bound invoke method
super(function() { return this.invoke(); });
return this.bind(this); // and bind our instance
// so we have access to instance values.
}
invoke() {
console.log("Hello, world!");
}
}
x = new MyClass();
x(); //prints "Hello, world!"

我主要采用this answer中的技术来实现这一目标。
使用此技术的一个有趣方面是,您可以将MyClass
命名为Callable
并删除invoke
方法 - 然后extends Callable
将变为可调用的任何类只要它有一个invoke()
方法。事实上......
class ExtensibleFunction extends Function {
constructor(f) {
// our link to Function is what makes this callable,
// however, we want to be able to access the methods from our class
// so we need to set the prototype to our class's prototype.
return Object.setPrototypeOf(f, new.target.prototype);
}
}
class Callable extends ExtensibleFunction {
constructor() {
// we build an ExtensibleFunction which accesses
// the late-bound invoke method
super(function() { return this.invoke(); });
return this.bind(this); // and bind our instance
// so we have access to instance values.
}
}
class CallableHello extends Callable {
invoke() {
console.log("Hello, world!");
}
}
class CallableBye extends Callable {
invoke() {
console.log("Goodbye cruel world!");
}
}
x = new CallableHello();
x(); //prints "Hello, world!"
y = new CallableBye();
y(); //prints "Goodbye cruel world!"

(当然,您可以通过在函数对象上设置属性来获得相同的效果,但我认为这更加一致)