views:

90

answers:

3

I often need to pass methods from objects into other objects. However I usually want the method to be attached to the original object (by attached I mean 'this' should refer to the original object). I know a few ways to do this:

a) In the object constructor: ObjectA = function() { var that = this; var method = function(a,b,c) { that.abc = a+b+c }}

b) In objectA which has been passed objectB: objectB.assign(function(a,b,c) { that.method(a,b,c) })

c) Outside both objects: objectB.assign(function(a,b,c) { objectA.method(a,b,c) })

I want to know if there is a simpler way to pass methods attached to their original objects.

A: 

As long as you call the other method with its appropriate owner object, this will always refer to the correct owner.

Luca Matteis
How do you propose I would do this? Pass the object and a string representing the method: `assign(object, 'method')`? I don't want the method name to be hard coded in the other object
peterjwest
+4  A: 

You can define a "createDelegate" method for all functions:

Function.prototype.createDelegate = function(scope) {
    var method = this;
    return function() {
        return method.apply(scope, arguments);
    }
}

And then use like:

var myDelegate = myFunction.createDelegate(myScope);

calling "myDelegate" will execute "myFunction" with "this" pointing to "myScope" and the same arguments as passed to myDelegate

ob1
Thanks, this is perfect! Although I don't understand it yet.
peterjwest
I edited a little, so maybe it's clearer now.If not, you can ask ...
ob1
It may also be worth looking at the current (1.7 RC 2) source of Prototype's `Function#bind`, which serves the same purpose as ob1's `createDelegate` but which went through quite a round of performance testing and optimization a few months back: http://github.com/sstephenson/prototype/blob/master/src/lang/function.js#LC108 Separately: I'd use `scope || undefined` rather than `scope || window`, to avoid the unnecessary browser-specificity and issues with `window` being overwritten. Calls to `apply` using `undefined` as the scope will use the global object.
T.J. Crowder
@peterjwest: *"Although I don't understand it yet."* It's basically doing what you were doing: Creating a function that, when called, will call the actual target function using the given context (`this` value). Sometimes it's more efficient to use a closure for this (where you have `var self = this` and then a function that uses `self.foo()` that closes over it), other times it's more efficient to create a delegate as with ob1's example -- and the latter is clearer to people who are relatively new to Javascript and/or who don't understand closures very well, which is of value.
T.J. Crowder
What kind of efficiency are you talking about: programming or performance? Could you give some examples of when either method is preferable?
peterjwest
@T.J. The provided implementation does this exact thing - creates a function which closes over the method, scope and callArgs.
ob1
@ob1: That's what I meant, that your implementation was one generic way to do this, which is frequently useful. I wasn't being critical. (In fact, one of those upvotes is mine. :-) )
T.J. Crowder
@peterjwest: Re efficiency: The great thing about the delegate approach that ob1's given you is that it's *so* easy to use and it Just Works. I use it a fair bit. Sometimes, though, if you're going to be creating closures for other reasons *anyway*, there's no need for the additional level of indirection -- instead, just do the `var self = this;` thing and then use `self` within the closures. Similarly, whenever I'm defining an API that accepts a callback, I *always* allow the caller to specify the context and use it if given; APIs that do that mean you don't have to create delegates at all.
T.J. Crowder
@T.J. Thanks for the clarification. Re: API callback, do you mean something like `method(a, b, c, callback, context)` and then have a default context such as `this`?
peterjwest
@peterjwest: Right. What the default context should be will depend on your use case (frequently you can just use what the caller gave you, because if it's `undefined`, `null`, or not an object, the Javascript interpreter will use the global object [`window`] automatically). But sometimes it's useful to have a different default. For instance, if I were doing a DOM event mechanism, I'd let people specify a context but would default to making `this` the element on which the event occurred.
T.J. Crowder
A: 

You can delegate if you like, or just call the object that lacks the method from the defined method scope-

instance.method.call(Other-Object/*,argument,...*/)

For example, if Array has a filter method, you can call it as if it were a method of a node list object.

var list=document.getElementsByTagName('p'), a=[], 

list= a.filter.call(list,function(itm){return itm.className=='sidebar'});
kennebec