3

I am trying to pass a method as a callback reference to a method of another class:

function A() {
    this.callback = function(){return 0;};
}

A.prototype.set = function(cb) {
    this.callback = cb;
};

A.prototype.test = function(){
    console.log("test " + this.callback());
};

function B(o) {
    this.prop = "blah B";
    o.set(this.blah);
}

B.prototype.blah = function() {
    console.log(this);
    return this.prop;
};

What I would expect from the execution

a = new A();
b = new B(a);
a.test();

is the result

>B { prop="blah B", blah=function()}
>test blah B

But instead the console shows

>A { cb=function(), set=function(), test=function()}
>test undefined

as if the method blah has been assigned to A for the execution..

Why am I getting this result?
And how can I get the result I expected?

0

4 Answers 4

3

"as if the method blah has been assigned to A for the execution", you almost got it, just replace "A" with "a" in your sentence. Indeed, by default, this refers to the instance which owns the method.

As it happens, o.set(this.blah) can be translated into a.callback = b.blah, which means that a.callback and b.blah now refer to the same function. In other words, the same function is now owned by both instances a and b :

a.callback() // "return (this -> a).prop" -> undefined
b.blah()     // "return (this -> b).prop" -> "blah B"

Roughly speaking, you need a way to "redirect" this to b inside callback.

You could use either a closure :

var me = this;
o.set(function () {
    // this -> a
    // me   -> b
    return me.blah();
});

Or bind (not supported by IE8 and below) :

o.set(this.blah.bind(this));
Sign up to request clarification or add additional context in comments.

2 Comments

The closure method doesn't work, it needs to be function(){return me.blah();} (as blah returns a value). I didn't know bind, it's an interesting one.. And it doesn't really matter for the IE compatibility, the rest of my code isn't compatible <IE8 anyway...
@ThierryJ. Thanks for your reply, I've fixed the answer accordingly.
2

Yes, that is how JavaScript's this keyword works - it refers to the current execution context, which is the object it is called on in this case. You are calling it as if it was a method of A. You need to call it as a method on your B instance. If you don't need to support IE8 or lower, you can use Function.prototype.bind to bind this in the function so it always points to the B instance. Otherwise, the A instance needs a reference to the B instance on which to call the method.

You could manually bind the method with a var self = this; hack. You could also do this in the B constructor:

function B(o) {
    var self = this;
    this.prop = "blah B";
    o.set(function() { self.blah(); });
}

But if you could expand a bit on what exactly you're trying to do, there might be a more elegant solution.

Comments

2

The problem is because of execution context - how this of a function in JavaScript is bound to. When the final line a.test() is called, the callback looks for a property called prop in the context of the object a. In your case, the object a did not have a prop property and hence the undefined. When you supply a prop to a then it works fine. Final fiddle with console.log here. Here the callback is called twice, once without the assignment of prop and the second with the assignment of the same:

function B(o) {
    this.prop = "blah B";
    o.set(this.blah);
    o.prop = this.prop;
}

Comments

1

When blah method is called inside the test method, this points to an A instance. Since A does not have a prop property it returns undefined.

You need to change the line o.set(this.blah); to this o.set(this.blah.bind(this));

Now blah is always bound to an instance of B and when test is called this.prop will return "blah B" which is what is intended.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.