snippetjavascriptCritical
How to access the correct `this` inside a callback
Viewed 0 times
howaccesscorrectthethiscallbackinside
Problem
I have a constructor function which registers an event handler:
However, I'm not able to access the
I also tried to use an object method instead of an anonymous function:
but it exhibits the same problems.
How can I access the correct object?
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', function () {
alert(this.data);
});
}
// Mock transport object
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// called as
var obj = new MyConstructor('foo', transport);
However, I'm not able to access the
data property of the created object inside the callback. It looks like this does not refer to the object that was created, but to another one.I also tried to use an object method instead of an anonymous function:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', this.alert);
}
MyConstructor.prototype.alert = function() {
alert(this.name);
};but it exhibits the same problems.
How can I access the correct object?
Solution
What you should know about
To learn more about
How to refer to the correct
Use arrow functions
ECMAScript 6 introduced arrow functions, which can be thought of as lambda functions. They don't have their own
Don't use
You actually don't want to access
Since
Explicitly set
It might look like you have no control over the value of
Every function has the method
In this case, we are binding the callback's
Note: When a binding context for jQuery, use
Set
Some functions/methods which accept callbacks also accept a value to which the callback's
The first argument is the callback and the second argument is the value
Note: Whether or not you can pass a value for
This object will be made the context of all Ajax-related callbacks.
Common problem: Using object methods as callbacks/event handlers
Another common manifestation of this problem is when an object method is used as callback/event handler. Functions are first-class citizens in JavaScript and the term "method" is just a colloquial term for a function that is a value of an object property. But that function doesn't have a specific link to its "containing" object.
Consider the following example:
The function
As already mentioned at the beginning, what
If the code was like the following, it might be more obvious that the function doesn't have an implicit reference to the object:
```
thisthis (aka "the context") is a special keyword inside each function and its value only depends on how the function was called, not how/when/where it was defined. It is not affected by lexical scopes like other variables (except for arrow functions, see below). Here are some examples:function foo() {
console.log(this);
}
// normal function call
foo(); // `this` will refer to `window`
// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`
// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`To learn more about
this, have a look at the MDN documentation.How to refer to the correct
thisUse arrow functions
ECMAScript 6 introduced arrow functions, which can be thought of as lambda functions. They don't have their own
this binding. Instead, this is looked up in scope just like a normal variable. That means you don't have to call .bind. That's not the only special behavior they have, please refer to the MDN documentation for more information.function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}Don't use
thisYou actually don't want to access
this in particular, but the object it refers to. That's why an easy solution is to simply create a new variable that also refers to that object. The variable can have any name, but common ones are self and that.function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}Since
self is a normal variable, it obeys lexical scope rules and is accessible inside the callback. This also has the advantage that you can access the this value of the callback itself.Explicitly set
this of the callback - part 1It might look like you have no control over the value of
this because its value is set automatically, but that is actually not the case.Every function has the method
.bind [docs], which returns a new function with this bound to a value. The function has exactly the same behavior as the one you called .bind on, only that this was set by you. No matter how or when that function is called, this will always refer to the passed value.function MyConstructor(data, transport) {
this.data = data;
var boundFunction = (function() { // parenthesis are not necessary
alert(this.data); // but might improve readability
}).bind(this); // <- here we are calling `.bind()`
transport.on('data', boundFunction);
}In this case, we are binding the callback's
this to the value of MyConstructor's this.Note: When a binding context for jQuery, use
jQuery.proxy [docs] instead. The reason to do this is so that you don't need to store the reference to the function when unbinding an event callback. jQuery handles that internally.Set
this of the callback - part 2Some functions/methods which accept callbacks also accept a value to which the callback's
this should refer to. This is basically the same as binding it yourself, but the function/method does it for you. Array#map [docs] is such a method. Its signature is:array.map(callback[, thisArg])The first argument is the callback and the second argument is the value
this should refer to. Here is a contrived example:var arr = [1, 2, 3];
var obj = {multiplier: 42};
var new_arr = arr.map(function(v) {
return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argumentNote: Whether or not you can pass a value for
this is usually mentioned in the documentation of that function/method. For example, jQuery's $.ajax method [docs] describes an option called context:This object will be made the context of all Ajax-related callbacks.
Common problem: Using object methods as callbacks/event handlers
Another common manifestation of this problem is when an object method is used as callback/event handler. Functions are first-class citizens in JavaScript and the term "method" is just a colloquial term for a function that is a value of an object property. But that function doesn't have a specific link to its "containing" object.
Consider the following example:
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = function() {
console.log(this.data);
};The function
this.method is assigned as click event handler, but if the document.body is clicked, the value logged will be undefined, because inside the event handler, this refers to the document.body, not the instance of Foo.As already mentioned at the beginning, what
this refers to depends on how the function is called, not how it is defined.If the code was like the following, it might be more obvious that the function doesn't have an implicit reference to the object:
```
Code Snippets
function foo() {
console.log(this);
}
// normal function call
foo(); // `this` will refer to `window`
// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`
// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}function MyConstructor(data, transport) {
this.data = data;
var boundFunction = (function() { // parenthesis are not necessary
alert(this.data); // but might improve readability
}).bind(this); // <- here we are calling `.bind()`
transport.on('data', boundFunction);
}array.map(callback[, thisArg])Context
Stack Overflow Q#20279484, score: 2314
Revisions (0)
No revisions yet.