views:

87

answers:

3

I want to do this in Javascript:

function Z( f )
{
  f();
}

function A()
{
  this.b = function()
  {
    Z( function () { this.c() } );
  }

  this.c = function()
  {
    alert('hello world!');
  }
}

var foo = new A();
foo.b();

It can be accomplished this way:

function Z( f )
{
  f();
}

function A()
{
  var self = this;
  this.b = function()
  {
    Z( function () { self.c() } );
  }

  this.c = function()
  {
    alert('hello world!');
  }
}

var foo = new A();
foo.b();

Is there a better way?

+3  A: 

Keeping a reference to the parent (like you have) is a good approach, however for your specific example there's no need for the anonymous wrapper, you can pass the function directly, like this:

var self = this;
this.b = function()
{
  Z(self.c);
}

You can test it out here, and without this wrapper there's actually no need for the self variable, you can just use this directly, like this:

this.b = function()
{
  Z(this.c);
}

You can test that version here.


Since there seems to be some confusion in the comments below, the above code maintains this for the question, if you want to maintain the this/context inside the callback as well, use .call() like this:

this.b = function()
{
  Z.call(this, this.c);
}

And for Z:

function Z( f )
{
  f.call(this);
}

You can test it here.

Nick Craver
Right, in this simple example, there is no reason for the wrapper. It shouldn't be hard to conceive of an example where it is necessary. (Changing parameters, etc.) If you don't wrap, though, I assume the closure is passed as one might expect?
Jonathan Swinney
without the anonymous wrapper, `c` is called with the wrong `this` reference.
Lee
@Jonathan - Yes, correct, in that case you'd want to pass a variable in just like you have `self` that gets you access to what's in the closure.
Nick Craver
@Lee - it's not, the demo shows this, *with* the wrapper `this` is an incorrect reference.
Nick Craver
I was hoping there was some kind of syntax for referring to parent closure; something like `Z( function() { parent.this.c } )`.
Jonathan Swinney
@Jonathan - There's `.bind()` in the latest specs though `.parent` is a different issue ;)
Nick Craver
@Nick - function `c` is a member of class `A`. It should be able to use `this` internally to access other members of `A`. When called through `Z` without the wrapper, this will not be the case. You can see this demonstrated [here](http://jsfiddle.net/UQpnX/).
Lee
@Lee - you're inside another context at *that* point, you're making up a *different* question :) In *that* case I'd use `.call()` like this: http://jsfiddle.net/nick_craver/UQpnX/1/
Nick Craver
@Nick - I assumed that `Z` was a placeholder for "any external function that takes a callback reference" as a parameter and (presumably) calls the callback at some point in the future. If you have a callback function that is a member of a class (and needs to be called with a proper `this` reference), and you don't have control over the implementation of `Z` (which is often the case), then you either have to use the anonymous wrapper function (as the OP did), or you need a formal encapsulation of some type (like the Delegate I've outlined below). Either is an appropriate solution.
Lee
@Lee - I don't disagree, what I *do* disagree with is your original comment that `this` was the wrong reference, it wasn't for the question. What `this` is *for* the callback in the `Z` invocation and what `this` is *inside* that callback are two different things :) I clarified above to hopefully remove any ambiguity.
Nick Craver
@Nick - agreed. It all depends on what you're expecting. When I see `c` defined as a *member* of `A`, I expect that `c` should always be called through a reference to an instance of `A`. Likewise, when I see `Z` defined in global scope, I assume that it should always be called globally. `Z` could just as well have been a method on another object (e.g. `q.Z(...)`), in which case I would have assumed that `Z` was intended to be called through a reference to an instance of `Q`. It's just one common perspective - clearly there are many possible interpretations.
Lee
A: 

There is a pattern that's often called "Delegate", which addresses this issue.

In javascript, a not-too-fancy implementation might look something like this:

/** class Delegate **/
var Delegate = function(thisRef, funcRef, argsArray) {
    this.thisRef=thisRef;
    this.funcRef=funcRef;
    this.argsArray=argsArray;
}
Delegate.prototype.invoke = function() {
    this.funcRef.apply(this.thisRef, this.argsArray);
}
/** static function Delegate.create - convenience function **/
Delegate.create = function(thisRef, funcRef, argsArray) {
    var d = new Delegate(thisRef, funcRef, argsArray);
    return function() {  d.invoke(); }
}

In your example, you would use it like this:

this.b = function() {
  Z( Delegate.create(this, this.c) );
}

you could also write functions that expect to receive a Delegate:

function Z( d ) {
    d.invoke();
}

then, in A, your impl of b becomes:

this.b = function() {
    var d = new Delegate(this, this.c);

    Z( d );
    SomeOtherFunc( d );
}

The Delegate just provides a simple, consistent way of encapsulating the this reference (which you've called self), within an object instance that can be dealt with like any other object instance. It's more readable, and it keeps you from having to pollute your function scope with superfluous variables like self. A fancier delegate implementation could have its own methods and other related state. It's also possible to build the delegate in such a way that it helps to minimize some scope-related memory management problems (though the code I've shown here is definitely not an example of that).

Lee
I don't think this works: `Z( function() { Delegate.create( this, c ) } );`. Without the wrapper, there is no need for `self` and no reason for the Delegate because it's shorter to write `Z( this.c );`.
Jonathan Swinney
@Jonathan - you've added an extra wrapper that's not in my example. it's just `Z( Delegate.create(this,c) )`; I expanded the Delegate example a little so it's more flexible, and clearer what's going on.
Lee
@Jonathan - `Z( this.c )` will work, as long as you don't care what object `this` references when running inside `c`. The code you've shown doesn't use `this` from within `c`, so it won't present any problems. If you check out (this slight modification of @Nick's example)[http://jsfiddle.net/UQpnX/], you'll see the potential problem, which is solved by the anonymous wrapper, in your original example -- or with a more formal construct like the Delegate. Either one is fine.
Lee
If you have to call `.invoke()` in your callback, why not use `.call()` or `.apply()` in both locations? It's a *much* simpler approach.
Nick Craver
@Nick - you *don't* have to call `invoke()` in your callback. That is *one* of the two examples I showed. The *first* example is by far the more common usage of this approach: the static `Delegate.create` returns a simple function. You can pass it off to anywhere that you could pass any other function. The second example simply shows an alternate usage, which is really only useful if you extend Delegate to include additional domain specific features -- and there are cases in which this can be a very desirable feature.
Lee
@Lee - Fair enough, though I disagree this would be very desirable many places, over `.call()` and `.apply()` directly. You should correct the example above though, `c` wouldn't be defined in your `Z( Delegate.create(this, c) );`, you need a `this.` qualifier there.
Nick Craver
+1  A: 

You can alternatively use

this.b = function()
{
    Z( (function () { this.c() }).apply(this) );
}
BrunoLM