Serge Seredenko
Serge Seredenko

Reputation: 3541

Javascript overloaded parenthesis

I want to be able to merge objects and functions in javascript. Currently I have such code:

var Functor = function(f, prop) {
  var inst = function() {return inst.__op__.apply(this, arguments)}
  inst.__op__ = f
  for (var key in prop)
    inst[key] = prop[key]
  return inst
}

Usage is like this:

var x = Functor(function(arg) {console.log(arg)}, {qwe:123})
console.log(x.qwe) // 123
x(321) // 321

But I have two problems: firstly, I don't like copying properties from prop argument to returned inst, I'd like there to be some kind of link to prop. Secondly, I want to be able to adress this in first Functor argument getting link to the inst. It seems that my problems can be solved reshaping Functor function for using with new keyword. But I can't figure out how to do that.

Important features are that I'm able to change __op__ property (which is kind of () operator) and that I can bind the result and get correct behaviour:

var x = Functor()
x.__op__ = console.log
var echo = x.bind(console)
echo(123) // 123

I would appreciate if somebody could help me with any of my problems.
Here's a fiddle with code copied there: http://jsfiddle.net/avmhbyzr/

Upvotes: 1

Views: 169

Answers (1)

Alan Tam
Alan Tam

Reputation: 2057

To avoid copying over all the properties from prop, you can set prop as the prototype of inst. Since inst is a function (not an ordinary object), you have no control of its constructor (it has to be Function), so the only way to change its prototype is via the internal __proto__ property.

The immediate drawback is, you lose access to all methods of a function, e.g. you cannot call x.call(). But you can solve it by chaining Function.prototype to prop. See the code below.

var Functor = function(f, prop) {
  var inst = function() {
      return inst.__op__.apply(this, arguments)
  }
  // Setup prototype chain: inst -> prop -> Function.prototype
  prop.__proto__ = inst.__proto__
  inst.__proto__ = prop

  inst.__op__ = f
  return inst
}

!function() {
    var x = Functor(null, {qwe:123})
    console.log(x.qwe) // 123

    // Method 1: bind the context to __op__
    x.__op__ = console.log.bind(console)
    x(321) // 321

    // Method 2: call x with the context
    x.__op__ = console.log
    x.call(console, 321) // 321
}()

As for the use of this, you are already correctly passing on the context to __op__ at inst via Function.prototype.apply.

The way to pass on a context is via calling x with a context (Method 2), or the more clumsy console.x = x; console.x(321). If you don't want to do this, you can always bind the context into __op__ (Method 1).

Keep in mind that when a function is executed, it needs both the context (this) and the arguments. The context is either permanently bound to the function (Method 1) or supplied in situ (Method 2).

Upvotes: 1

Related Questions