1

In the vain of this question.

Is it at all possible for an object to also be a function.

For example I have the object person which is an instance of some object.

Is it possible for me to call person(...) and have some return come back to me?

This is where person has not been defined as a function to start with.

So for example I have the "class" Person:

class Person {
     // .. some class functions
}

Then I create a new one

let person = new Person();

Can I call person() and affect the actual object behavior? Or is that not possible at all and person itself will have to be defined as a function to begin with?

Community
  • 1
  • 1
Naftali
  • 144,921
  • 39
  • 244
  • 303
  • 2
    you can add properties to a function, which is an object, but you can't call an object. – dandavis Mar 17 '16 at 19:28
  • @dandavis yes I know that. This is if you have just a an object instance. – Naftali Mar 17 '16 at 19:28
  • Not yet. Closest you can come AFAIK is to have a custom class inherit from `Function`, ie `myClass extends Function`. NOTE: requires native implementation, so current edge, ff, or chrome only. – Jared Smith Mar 17 '16 at 19:30
  • what function would you be calling? – dandavis Mar 17 '16 at 19:30
  • You want a constructor which produces functions? How would you define the code of the function? – Oriol Mar 17 '16 at 19:32
  • Try `var fn = function(){alert(fn.item)}; fn.item='hello'; fn();` –  Mar 17 '16 at 19:36
  • I'm going to go with short answer no on this one. javascript does not allow you to pass parameters to objects, only defined functions. – Alden Be Mar 17 '16 at 19:42
  • 1
    Possible duplicate of [How to convert an "object" into a function in JavaScript?](http://stackoverflow.com/questions/124326/how-to-convert-an-object-into-a-function-in-javascript) – Hatchet Mar 17 '16 at 19:47
  • @WashingtonGuedes is it possible to do that and it would affect the values of the `fn` object itself? and can you pass vars? – Naftali Mar 17 '16 at 19:51
  • @Hatchet I updated my question with more explanation. – Naftali Mar 17 '16 at 19:55
  • @dandavis I updated my question with more of an explanation of what I mean. – Naftali Mar 17 '16 at 19:56
  • I don't think you can alter [[Call]] after object creating. It's not an invariant, though, so not completely impossible. – Oriol Mar 17 '16 at 20:01

1 Answers1

1

Short answer, No. Yes! See proxy solution below.

In section 9.2.1 of the spec it outlines Function objects' internal [[Call]] property that allows that object to be called as a function. There is currently no way I know of to modify that property, i.e. make an object into a function after its creation. It will be possible with a fully compliant implementation of ES 2015 to subclass Function and have callable objects (currently works in FF, for example). Even then, I didn't try anything more than making a paper-thin layer over the Function constructor itself. However, ES 2015 also includes Proxies, and one of the proxy traps is 'apply'.

class Person {
  constructor(arg, body) {
    this.fn = function(a) { this.a = a; };
    return new Proxy(this, {
      apply: function(target, thisArg, args) {
        return target.fn.apply(target, args);
      }
    });
  }
}
var person = new Person();
person(3);
person.a; //3

Tested in Chrome 49. Fails in FF unless Person extends Function and makes a call to super:

class Person extends Function {
  constructor() {
    super();
    this.fn = function(a) { this.a = a; };
    return new Proxy(this, {
      apply: function(target, thisArg, args) {
        return target.fn.apply(target, args);
      }
    });
  }
}
var person = new Person();
person(3);
person.a;

According to MDN, the part about requiring the super call is correct at least (for subclasses) but I am unsure whether Proxies should let you call a non-function the way chrome lets you.

UPDATE

I opened another question about which behavior is correct. For those too lazy to follow a hyperlink the calling of a non-function is a bug in some specific versions of chrome that has since been fixed.

Community
  • 1
  • 1
Jared Smith
  • 19,721
  • 5
  • 45
  • 83
  • Updated the question with more explanation. – Naftali Mar 17 '16 at 19:54
  • Hmm interesting. Is it even possible to extend `Function`? – Naftali Mar 18 '16 at 13:42
  • This is a very cool answer. It would be interesting to try to figure our how to extend `Function` but to keep some `this` binding intact without the extra magic – Naftali Mar 18 '16 at 13:47
  • @Neal it is possible with a native es 2015 class implementation (current chrome, ff, edge). As for the `this` part, you might be able to work it with a bound call to `super`. – Jared Smith Mar 18 '16 at 16:04
  • Never mind about super. Updated answer to include a solution using proxies. – Jared Smith Mar 18 '16 at 16:35
  • Jared -- with this solution however (with the proxys) Can I have other functions a part of the `Person` class? or would they all have to be defined in the proxy? – Naftali Mar 18 '16 at 17:56
  • @Neal no the proxy simply wraps an object (in this case, a function object). So a person could be both a function and an object with methods. You can even assign values (including methods) after initialization, the Proxy will pass through to the underlying object any operations it doesn't have a trap set for (including assgnment). – Jared Smith Apr 05 '16 at 13:34