Reputation: 1664
I have target object
function Foo() {
this.someVar = 'some var';
};
Foo.prototype.callback() {
console.log(this);
};
And object, that will call this callback
function Bar(callback) {
this.callback = callback;
};
Bar.prototype.onSomeAction = function() {
this.callback();
};
And initial code
foo = new Foo();
bar = new Bar();
bar.callback = foo.callback;
bar.onSomeAction();
Result: i have logged to console Bar()'s context instead of Foo(). How can i get context of Foo() in the Foo() callback?
PS: I tried closures
Foo.prototype.callback() {
var foo = this;
return function(foo) {
console.log(foo);
};
};
but it does nothing. I have not fully understanding of the closures :(
Upvotes: 0
Views: 75
Reputation: 2246
The reason your original code didn't work is that the value of this
inside of a method call is the value of the object it's being called on. That means when you say:
bar.callback = foo.callback;
And then you call:
bar.callback();
The code defined here:
Foo.prototype.callback = function () {
console.log(this);
};
gets called with this
being a reference to bar
because bar
is to the left of the .
on the method call. So whenever you assign a function as an object property, calling it on that object will call it with the object as this
.
You could also have written:
function callback() {
console.log(this);
}
bar.callback = callback;
bar.callback();
And you would find that this
still references bar
.
In fact, if you call the plain function callback();
as defined above, you'll find that this
is a reference to the global object, usually window
in web browsers. That's because all global variables and functions are properties of window
, so callback();
is implicitly window.callback();
The fact that the value of this
depends on what object is calling a function can be a problem when passing callbacks around, since sometimes you want this
to reference the original object the function was a property of. The bind method was design to solve this problem, and Yuri Sulyma gave the right answer:
bar.callback = foo.callback.bind(foo);
However, the way you would do this using closures is to capture an instance of Foo within an anonymous function that calls the correct method on the correct object:
foo = new Foo();
bar = new Bar();
bar.callback = function () {
foo.callback();
};
bar.onSomeAction();
Which is essentially what bind does. In fact, we call write our own naive version of bind using a closure:
Function.prototype.bind = function (obj) {
var fn = this;
return function () {
fn.call(obj);
};
};
call
let's you call a function with the value of this
explicitly defined. This allows you to "set the context" the function is called in so that it's the same as calling obj.fn()
when you call bar.callback()
. Since when we call foo.callback.bind(foo);
, obj
is foo
and fn
is foo.callback
, the result is that calling bar.callback()
becomes the same as calling foo.callback()
.
That's where Dalorzo's answer comes from. He uses call
to explicitly set the context.
There's also another function for setting the context called apply
that also takes an array representing the arguments for the function as its second argument. This allows us to write a more complete version of bind by taking advantage of the special arguments
variable:
Function.prototype.bind = function (obj) {
var fn = this;
return function () {
fn.apply(obj, arguments);
};
};
Upvotes: 2
Reputation: 413
bar.callback = foo.callback.bind(foo);
You can polyfill Function.prototype.bind()
if necessary: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
Upvotes: 0
Reputation: 20014
Try using these changes:
Use call
to set context:
bar.onSomeAction.call(foo);
And I think your callback
function needs to change to:
Foo.prototype.callback=function() {
console.log(this);
};
Upvotes: 0