56

Is it possible to make an object callable by implementing either call or apply on it, or in some other way? E.g.:

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

...

obj (a, b);
Septagram
  • 9,425
  • 13
  • 50
  • 81
  • 4
    No, that's not possible. An object has to be "born" callable as an instantiated function. – Pointy Oct 12 '13 at 15:37
  • possible duplicate of [Constructor for callable object in JavaScript](http://stackoverflow.com/questions/12656079/constructor-for-callable-object-in-javascript) – Hans Z Oct 12 '13 at 15:40
  • 2
    @HansZ not really, my question is about making an *existing* object callable, not creating a new one. – Septagram Oct 12 '13 at 15:51
  • Very much related: [Can you make an object non-callable?](https://stackoverflow.com/q/29680473/1048572) – Bergi Sep 29 '20 at 14:10
  • Here is an elegant answer https://stackoverflow.com/questions/36871299/how-to-extend-function-with-es6-classes?answertab=votes#tab-top – vovchisko Jun 20 '21 at 19:06
  • keep in mind that a function with properties (or vice versa) is very unintuitive to work with – chantey Aug 23 '21 at 08:16

5 Answers5

40

No, but you can add properties onto a function, e.g.

function foo(){}
foo.myProperty = "whatever";

EDIT: to "make" an object callable, you'll still have to do the above, but it might look something like:

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

And then you'd just use the "func" function instead of the object. Really all this method does is copy properties between two objects, but...it might help you.

Rodrigo Rodrigues
  • 7,545
  • 1
  • 24
  • 36
Max
  • 4,882
  • 2
  • 29
  • 43
  • Yes but how do you copy the prototype chain? I need an function/object that is callable, but actually an instance of a type. – Nick Sotiros Jan 26 '18 at 11:18
  • 14
    I wish there was symbol that you could set to make an object callable like `obj[Symbol.invoke] = function(args) { };` – Nick Sotiros Jan 26 '18 at 11:25
  • @NickSotiros maybe `Object.setPrototypeOf(func, object)`? – Rodrigo Rodrigues Feb 04 '19 at 06:19
  • 1
    FYI: In NodeJS, this will fail if the property name conflicts with a readonly property on the function object, such as `name`. Example: `(function(){}).name='';` __`TypeError: Cannot assign to read only property 'name' of function 'function(){}'`__ – James Wilkins Aug 10 '19 at 01:01
  • @NickSotiros To make an object "callable," you can [return a `Proxy`](https://gist.github.com/arccoza/50fe61c8430fc97a463bf6b8960776ce) from the object's constructor. – Anderson Green Sep 17 '21 at 22:10
27

ES6 has better solution for this now. If you create your objects in a different way (using class, extending 'Function' type), you can have a callable instance of it.

See also: How to extend Function with ES6 classes?

0xc0de
  • 8,028
  • 5
  • 49
  • 75
  • I would not call it better. That solution supported better by major browser what actually force people to use that one instead of alternatives. But it does not make this solution better at all. This solution is counter-intuitive and not compatible with alternative OOP approaches. – Kos Jul 16 '17 at 13:54
  • @wandalen I'm afraid I don't understand what you want to say. In any way, a more readable, cleaner, and (in fact it is) intuitive (at least to those coming from c++ or java - OOP) solution is better, isn't it? why do you think a class extending 'Function' to create a callable, isn't intuitive? – 0xc0de Jul 16 '17 at 14:36
  • 2
    you get one `extends` per "class", so you'd rather use it to do a workaround for the language, reuse code you probably should have aggregated inside the class or should use it only for a natural connection in the domain objects? – Azder Mar 18 '18 at 11:13
  • 1
    It would've been nice if you also provided a code snippet. – Arad Alvand Dec 10 '21 at 14:07
10

Following the same line of @Max, but using ES6 extensions to Object to pass all properties and prototype of an object obj to the callable func.

Object.assign(func, obj);
Object.setPrototypeOf(func, Object.getPrototypeOf(obj));
Rodrigo Rodrigues
  • 7,545
  • 1
  • 24
  • 36
  • 1
    For others' sake, I found this answer referenced from https://stackoverflow.com/a/74124360 – Moh Aug 15 '23 at 13:56
6

Others have provided the current answer ("no") and some workarounds. As far as first-class support in the future, I suggested this very thing to the es-discuss mailing list. The idea did not get very far that time around, but perhaps some additional interest would help get the idea moving again.

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

Brasten Sager
  • 128
  • 1
  • 6
0

"CALLABLE OBJECTS"
I haven't seen mention of this type of answer yet.. but this is how I do "callable" objects:
<< PSEUDO CODE >>

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

short example defining first, simplest and preferred method if I just want a "callable object," in my definition:

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

short example of nameless object being run within a function's return, with parameters (note parameters works in the first example too):

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 ")

and that's pretty much it You could even get weirder with it and do nameless functions that auto-run themselves and produce an output right away... for no reason, lol. ... hit F12 and copy this code into your browser console and press enter, you'll get an output right away with the full string:

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

You could even pass in the string of "b" in the final parenthesis for a different output from the default "o".

Also, each of my examples (minus the pseudo code first example) are easily copy/paste-able into the browser console for quick testing -- it's a nice place to experiment with JS.

In summary -- this is how I like to do "callable objects"
It's much better than
SWITCH(){CASE:BREAK;};
statements and
IF{}ELSE IF(){}ELSE IF(){};
chains.

Fox
  • 1
  • 1
  • (also you can do callable arrays if you don't mind using indexes)------- (this is also copy-paste-able into your console)------ [()=>"zero",()=>"one",()=>"two"][2]() – Fox Jun 30 '22 at 18:16
  • they are not callable. they contain functions, which are callable. also applies for the answer - they are objects with functions in it, not callable. But if you wanna really get creative with arrays, you may create your own iterable and "call" it (with only int arguments) like arr[8]. or maybe use proxies (with only string argument) – FLAW Sep 29 '22 at 22:42