你能让一个对象“可调用”吗?

57

通过实现callapply方法或以其他方式,是否可以使对象可调用?例如:

var obj = {};
obj.call = function (context, arg1, arg2, ...) {
    ...
};

...

obj (a, b);

4
不,那是不可能的。一个对象必须作为可调用的函数被“创建”。 - Pointy
可能是JavaScript中可调用对象的构造函数的重复问题。 - Hans Z
2
@HansZ 不是很清楚,我的问题是如何使一个已有的对象可调用,而不是创建一个新的对象。 - Septagram
这里有一个优雅的答案 https://dev59.com/YFoU5IYBdhLWcg3w_KiO?answertab=votes#tab-top - vovchisko
记住,具有属性的函数(或反之亦然)在使用时非常不直观。 - chantey
5个回答

40
不可以,但你可以给函数添加属性,例如:
function foo(){}
foo.myProperty = "whatever";

编辑:要将一个对象“制作”为可调用的,您仍然需要执行上述操作,但它可能看起来像:

// Augments func with object's properties
function makeCallable(object, func){
    for(var prop in object){
        if(object.hasOwnProperty(prop)){
            func[prop] = object[prop];
        }
    }
}

那么你只需要使用"func"函数而不是对象。这个方法实际上只是在两个对象之间复制属性,但是...它可能会对你有所帮助。


是的,但是如何复制原型链呢?我需要一个可调用的函数/对象,但实际上是一个类型的实例。 - Nick Sotiros
15
我希望有一种符号可以设置为使对象可调用,例如 obj[Symbol.invoke] = function(args) { }; - Nick Sotiros
@NickSotiros 可以尝试使用 Object.setPrototypeOf(func, object) 吗? - Rodrigo Rodrigues
1
请注意:在NodeJS中,如果属性名与函数对象上的只读属性(例如name)发生冲突,则此操作将失败。例如:(function(){}).name='';会导致以下错误:TypeError: Cannot assign to read only property 'name' of function 'function(){}' - James Wilkins
1
@NickSotiros 要使一个对象“可调用”,您可以从对象的构造函数中 返回一个 Proxy - Anderson Green

28

ES6现在有更好的解决方案。如果您以不同的方式创建对象(使用classextend 'Function'类型),则可以拥有可调用的实例。

另请参见:如何使用ES6类扩展函数?


我不会称它为更好的解决方案。虽然这个解决方案被主流浏览器支持,但实际上迫使人们使用它而非其他替代方案。但这并不意味着这个解决方案更好。这个解决方案不符合直觉,并且与其他面向对象编程方法不兼容。 - Kos
@wandalen,恐怕我不明白你想说什么。无论如何,更易读、更清晰、更直观(至少对于那些来自C++或Java-OOP的人)的解决方案是更好的,不是吗?你为什么认为通过扩展“Function”类来创建可调用对象不直观呢? - 0xc0de
2
每个“类”只能使用一个extends,所以您最好将其用于语言的解决方法,重用代码,您可能应该将其聚合在类内部,或者仅在域对象中进行自然连接。 - Azder
1
如果您也提供了代码片段,那就太好了。 - aradalvand

13

继@Max的思路,但使用ES6扩展Object来传递对象obj的所有属性和原型到可调用的func

Object.assign(func, obj);
Object.setPrototypeOf(func, Object.getPrototypeOf(obj));

1
为了他人的利益,我在https://dev59.com/YFoU5IYBdhLWcg3w_KiO#74124360找到了这个答案的参考。 - Moh

6

其他人已经提供了当前的答案(“否”)和一些解决方法。至于未来的一流支持,我曾向es-discuss邮件列表建议过这个问题。那时候这个想法没有得到很大的推进,但也许一些额外的兴趣会帮助再次推进这个想法。

https://esdiscuss.org/topic/proposal-default-object-method


0

“可调用对象”
我还没有看到提到这种类型的答案..但这就是我如何处理“可调用”对象:
<< 伪代码 >>

{...objectWithFunctionsInside}[keyString](optionalParams)

简短的示例定义了第一种、最简单和首选的方法,如果我只想要一个“可调用对象”,在我的定义中:

let obj = { 
  o:()=>{return("oranges")},
  b:()=>{return("bananas")},
  s:"something random here, doesn't have to be functions"
}
obj["o"]()

以下是一个短的示例,展示了在函数返回中运行无名对象,并带有参数(请注意,参数在第一个示例中也适用):

function autoRunMyObject(choice,param){
  return{
    o:(p)=>{return(p+"oranges")},
    b:(p)=>{return(p+"bananas")},
  }[choice](param)
}
autoRunMyObject("b","orange you glad I didn't say ")

就是这样了。 您甚至可以更加奇怪,使用匿名函数自动运行并立即生成输出... 没有任何原因,哈哈。 ... 按F12并将此代码复制到浏览器控制台中,然后按Enter,您将立即获得带有完整字符串的输出:

((autoparam="o")=>{return{
  o:(p)=>p+"oranges",
  b:(p)=>p+"bananas",
}[autoparam]("I guess this time it's ")})()

你甚至可以在最后一个括号中传入字符串"b",以获得与默认值"o"不同的输出结果。

此外,我的每个示例(除了伪代码第一个示例)都可以轻松地复制/粘贴到浏览器控制台中进行快速测试——这是一个很好的实验JS的地方。

总之——这就是我喜欢做“可调用对象”的方式。
它比
SWITCH(){CASE:BREAK;};
语句和
IF{}ELSE IF(){}ELSE IF(){};
链要好得多。


(如果您不介意使用索引,也可以执行可调用数组) (这也可以复制并粘贴到您的控制台中) [() =>“零”,() =>“一”,() =>“二”] 2 - Fox
它们不可调用。它们包含可调用的函数。对于答案也适用-它们是带有函数的对象,不可调用。但是,如果你想真正创造性地使用数组,你可以创建自己的可迭代对象并像arr[8]一样“调用”它(仅使用int参数)。或者也可以使用代理(仅使用字符串参数)。 - FLAW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接