Javascript callbacks losing 'this'
Solution 1
function CheckBox(input_id) {
this.id = input_id;
this.doSomething = $.proxy( this.doSomething, this );
$('#some-element').click(this.doSomething);
}
The "javascript equivalent" of this is Function#bind
but that is not available in every browser and since it seems you are using jQuery I am using the jQuery equivalent $.proxy
Solution 2
Your problem is with this line: $('#some-element').click(this.doSomething);
Why this is a problem
JavaScript methods don't know anything about the object that should be assigned to this
, it's set when the method is called either explicitly (with myFunction.call(obj)
) or implicitly (when called using obj.myFunction()
).
For example:
var x = {
logThis: function () {
console.log(this);
}
};
x.logThis(); // logs x
x.logThis.call(y); // logs y
var func = x.logThis;
func(); // logs window: the fallback for when no value is given for `this`
In your case, you're passing this.doSomething
to jQuery, which is then explicitly calling it with the element that was clicked as the value of this
. What's happening is (a slightly more complex version of) this:
var callback = this.doSomething;
callback.call(anElement, anEvent);
The solution
You need to make sure that doSomething
is called with the right value of this
. You can do that by wrapping it in another function:
var cb = this;
$('#some-element').click(function() {
return cb.doSomething();
});
jQuery provides a proxy
function lets you do this more simply:
$('#some-element').click(jQuery.proxy(this.doSomething, this));
Solution 3
Others have already explained the causes of the problem and how to fix it with jQuery. What's left is how you fix it with standard JavaScript. Instead of ...
$('#some-element').click(this.doSomething);
... you write:
document.getElementById('some-element').addEventListener('click', this.doSomething.bind(this));
This changes the context of this
inside doSomething
. You can also do that with anonymous functions - instead of ...
$('#some-element').click(function(event) {
console.log(this);
});
... you write:
document.getElementById('#some-element').addEventListener('click', (function(event) {
console.log(this);
}).bind(this));
That has been very useful to me in projects with lots of callbacks, e.g. in Node.js (where you don't have to care about outdated browsers).
Edit: getElementById()
and addEventListener()
instead of $(...).click(...)
.
Anyone
Updated on June 07, 2022Comments
-
Anyone almost 2 years
I have an issuer where I lose the
this
inside this object. The output of the following piece of JavaScript gives me"some-id"
and thenundefined
. When I usethis
inside a callback function, the scope goes out of the object and it cannot usethis
any more. How can I get the callback to use 'this' or at least have access to the object?Since I will make multiple objects, I won't be able to create a 'static' like storage.
Here is my test code that you can use to reproduce my problem. What I would like to have is
CheckBox.doSomething()
to return the value ofthis.id
which should matchsome-id
for this test case.function CheckBox(input_id) { this.id = input_id; this.doSomething(); $('#some-element').click(this.doSomething); } Checkbox.prototype.doSomething = function() { alert(this.input_id); } var some_box = new CheckBox('some-id'); some_box.doSomething(); $('#some-element').click();
I can't even get this to work as I want it to:
function CheckBox2(input_id) { this.id = input_id; alert(this.id); } CheckBox2.prototype.doSomething = function() { alert(this.input_id); } var some_box = new CheckBox2('some-id'); some_box.doSomething();
-
Lucero almost 12 yearspossible duplicate of scope-of-this-in-javascript
-
Esailija almost 12 years@Lucero that technique is only available for dynamically created functions, not those in prototype.
-
Felix Kling almost 12 yearspossible duplicate of jQuery/javascript events - prototype event handler
-
VLAZ about 2 years
-
-
Anyone almost 12 yearsIn this case
some_box.doSomething();
is already giving me 'undefined' when I call it.. How is that possible? I'm not using any callbacks there -
Stan almost 12 yearsThere's a typo in your code: In the constructor function you're setting
this.id
but in thedoSomething
function you're referencingthis.input_id
-
Anyone almost 12 yearsyes I can see that.. Too much javascript for 1 day :3 Thanks a lot!! Now I understand why I had so many issues with undefined variables earlier with different methods of creating objects. Thanks for the clear examples
-
Esailija almost 12 yearsI'd put more emphasis on using $.proxy, it allows your class to be extendable and keep all functions neatly in prototype
-
Stan almost 12 years@Esailija: I think it's more beneficial to understand why the code doesn't work, rather than just knowing a specific way of solving the problem. If you're going to do tricks like
this.func = $.proxy(this.func, this)
then you should really understand how and why it works, which I'm sure you do, but others reading your answer might not. -
Esailija almost 12 years@georgebrock I didn't mean to compare to my answer, just wanted to point out that saying function binding is merely a simpler way is an understatement because it has a lot of other benefits as well. Regardless, yours is obviously a better answer.