Reputation: 3456
The following exemples use the closure mechanism inside a for loop
I do know the purpose of closures but I'm not able to tell what is the best choice between the following pieces of code and in particular why one would be better than the other ?
// First case: Closure wraps the ajax call
$('#container').on('click', 'a.log', function (e) {
_t = this;
for (var i = 0; i < 5; i++) {
(function (j) {
$.ajax({
url: "/logger",
context: _t
}).done(function () {
$(this).addClass("done" + j);
});
})(i);
};
});
// -------------------------------------------------------
// Second case :closure wraps the ajax callback function
$('#container').on('click', 'a.log', function (e) {
for (var i = 0; i < 5; i++) {
$.ajax({
url: "/logger",
context: this
}).done(
(function (j) {
return function () {
$(this).addClass("done" + j);
};
})(i)
);
};
});
I wish someone could explain me this precisly.
Thank you for your attention & time spent.
Upvotes: 0
Views: 50
Reputation: 664423
Approach #1 focuses on the fact that the IEFE is used to preserve the loop iteration variable. You might even write it on one line - the whole body of the loop goes into the function:
for (var i=0; i<5; i++) (function(j) {
…
})(i);
Approach #2 focuses more on the fact that the IEFE is used for providing j
in the scope of the callback closure - you could call it (function makeCallback(j){ … })(i);
.
In general, use what is better readable to you. You might always want to go with #1 as it makes creating other closures easier.
In this specific case, #1 led to a mistake: The this
value you're passing as the context
argument to the ajax function is not the one you expected from the event handler, but the one of the IEFE - undefined
(or window
in sloppy mode). You either will want to go with #2, or pass it explicitly:
for (var i=0; i<5; i++) (function(j) {
…context:this…
}).call(this, i);
// or
for (var i=0; i<5; i++) (function(el, j) {
…context:el…
})(this, i);
Upvotes: 1
Reputation: 5781
You could also consider moving out the immediately-invoked function expression to a separate function:
$('#container').on('click', 'a.log', function (e) {
for (var i = 0; i < 5; i++) {
doLogging(i, this);
};
});
function doLogging(i, clickedElement) {
$.ajax({
url: "/logger",
context: clickedElement
}).done(function () {
$(clickedElement).addClass("done" + i);
});
}
I think it's a lot easier to understand what's going on, and you avoid having to introduce a new variable name 'j'.
You don't have to send in the clicked element, you could call doLogging using apply():
doLogging.apply(this, [i]);
In which case this
inside doLogging()
will refer to the clicked element.
Upvotes: 0