I have a constructor that registers an event handler:
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 cannot access the data
property of the object created within the callback. It looks like this
does not refer to the created object, but to another object.
I also tried using object methods instead of anonymous functions:
function MyConstructor(data, transport) { this.data = data; transport.on('data', this.alert); } MyConstructor.prototype.alert = function() { alert(this.name); };
But it also has the same problem.
How to access the correct object?
P粉3114235942023-10-12 09:02:51
to bind the a>()
function. bind()
functionfunction MyConstructor(data, transport) {
this.data = data;
transport.on('data', ( function () {
alert(this.data);
}).bind(this) );
}
// Mock transport object
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// called as
var obj = new MyConstructor('foo', transport);
If you are using Underscore.js - http://underscorejs.org/#bind
transport.on('data', _.bind(function () { alert(this.data); }, this));
function MyConstructor(data, transport) {
var self = this;
this.data = data;
transport.on('data', function() {
alert(self.data);
});
}
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
P粉9680081752023-10-12 00:55:03
What you should know about this
this
(aka "context") is a special keyword within each function whose value depends only on how the function is called, not how/when/what Where it is called it is defined. It is not affected by lexical scope like other variables (except 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
, please view the MDN documentation .
this
ECMAScript 6 introduces arrow functions, which can be thought of as lambda functions. They do not have their own this
binding. Instead, this
is looked up in scope just like a normal variable. This means you don't have to call .bind
. This isn't their only special behavior, see the MDN documentation for more information.
function MyConstructor(data, transport) { this.data = data; transport.on('data', () => alert(this.data)); }
this
You don't actually want to access this
specifically, but access the object it refers to. That's why a simple solution is to simply create a new variable that also references the object. Variables 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 follows lexical scoping rules and can be accessed inside the callback. This also has the advantage that you can access the this
value of the callback itself.
this
- Part 1It may seem like you have no control over the value of this
since its value is set automatically, but that's not actually the case.
Every function has method .bind
[docs], which returns a new function where this
Bind to a value. This function has exactly the same behavior as the function you call .bind
, except that this
is set by you. No matter when or how the 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 example, we bind the callback's this
to the value of this
of MyConstructor
.
Note: When binding the context of jQuery, use jQuery .proxy
[docs] replace. The reason for this is so that you don't need to store a reference to the function when unbinding the event callback. jQuery handles this internally.
this
- Part 2Some functions/methods that accept callbacks also accept a value that the callback's this
should reference. This is basically the same as binding it yourself, but the function/method does it for you. Array#map
< em>[docs]This is the method. Its signature is:
array.map(callback[, thisArg])
The first parameter is the callback, and the second parameter is the value that this
should reference. 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 argument
Note: Whether it is possible to pass a value for this
is usually mentioned in the documentation for that function/method. For example, jQuery's $.ajax
method [docs] describes an option called context
:
Another common manifestation of this problem is when an object method is used as a callback/event handler. Functions are first-class citizens in JavaScript, and the term "method" is just a colloquial term for a function, which is the value of an object's property. But the 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); };
Function this.method
is specified as the click event handler, but if document.body
is clicked, the logged value will be undefined
because in In the event handler, this
refers to 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.
It might be more obvious that the function has no implicit reference to the object if the code looks like this:
function method() { console.log(this.data); } function Foo() { this.data = 42, document.body.onclick = this.method; } Foo.prototype.method = method;
Same as mentioned above: use .bind to explicitly bind this
to a specific value if available
Or explicitly call the function as a "method" of the object by using an anonymous function as a callback/event handler and assigning the object (document.body.onclick = this.method.bind(this);
) to another variable:
Or use arrow functions: var self = this;
document.body.onclick = function() {
self.method();
};
document.body.onclick = () => this.method();