Reputation: 3541
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
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