Reputation:
How can a property be assigned during object construction with a value generated asynchronously?
I'm trying to assign a property to an object during construction that needs to be retrieved via AJAX:
//define module prototype
Library.Module = function () {
this.resources = {};
for (var i = 0; i < arguments.length; i++) {
// assume that Library.require makes a request and
// then executes this callback
Library.require(arguments[i], function (resource) {
// ! the problem seems to be that 'this' is undefined
// in the scope of the callback
this.resources[resource.location] = resource;
});
}
};
I think the intention of this code is rather clear - The problem is that this
appears to be undefined in the scope of the callback function.
Upvotes: 1
Views: 51
Reputation: 751
As seen in the following article https://blog.engineyard.com/2015/mastering-this-in-javascript and following discussion in comments, a possible solution would be to store this in a variable higher in the scope to use it in the callback.
Therefore a possible solution could be :
Library.Module = function () {
var _this = this;
_this.resources = {};
for (var i = 0; i < arguments.length; i++) {
// assume that Library.require makes a request and
// then executes this callback
Library.require(arguments[i], function (resource) {
_this.resources[resource.location] = resource;
});
}
};
Useful snippet from the article cited:
Managing this in a callback
And that’s it: those are the four ways to set a function’s this value. Those four rules are not too hard to remember, but there is a common pitfall you should know about. I’m talking about callbacks. It’s easy to be writing a callback and not even know it, like in setTimeout:
setTimeout(function() { $(‘button’).addClass(‘red’); }, 1000);
The setTimeout function accepts a callback, but since it’s not using one of the four rules for setting context, this defaults to the global window object. That’s fine in the example above, but becomes a bug in code like this:
$('button').on('click', function() { setTimeout(function() { // Uh oh! `this` is the global object! $(this).addClass('clicked'); }, 1000); });
We’re expecting $(this) to refer to the button that was clicked, but it doesn’t, since this defaults to the global window object. One way to solve this issue is to store our desired value of this in a local variable and then simply use that variable in a child scope:
$('button').on('click', function() { var _this = this; setTimeout(function() { $(_this).addClass('clicked'); // All better }, 1000); });
Of course there are many ways to accomplish the same thing. You could use .bind(), .call(), or a number of other options. Choose what works best for each individual situation.
Upvotes: 1