Coffeescript 'this' inside jQuery .each()
I have some coffeescript like the following:
class foo:
@bar = 'bob loblaw'
processRows: ->
$("#my-table>tr").each ->
id = $(this).attr("id")
@processRow id
processRow: (id) ->
console.log @bar + id
So my problem is: I need this
to reference the .each
context inside the loop to get at id
, but I also would like this
to reference the class instance inside foo.processRow()
---which it does not currently do.
Using something like _this = this
outside the .each
function and passing it around isn't a great solution, either, since I reference many class variables inside processRow
.
Any thoughts? Am I missing something obvious? Thanks!
Solution 1:
jQuery.each
passes the current element as second parameter of the callback, so you don't have to reserve this
for jQuery:
processRows: ->
$("#my-table>tr").each (index, element) =>
id = $(element).attr("id")
@processRow id
Notice the use of the fat arrow (=>
) syntax for the callback function; it binds the function's context to the current value of this
. (this
in the callback function is always the same this
as the one at the time you defined the function.)
Solution 2:
You say
Using something like
_this = this
outside the.each
function and passing it around isn't a great solution, either, since I reference many class variables inside processRow.
This is the most efficient solution, though. JavaScript's this
is a strange beast; you can keep it fixed inside of a nested function using the =>
operator, as arnaud576875 sugests in his answer (which is elegant but inefficient), or you can copy this
to another variable (which is efficient but inelegant). The choice is yours.
Note that some modern browsers support a bind
method on every function, which is more efficient than CoffeeScript's =>
. There's an open ticket to have =>
use the native bind
when available: https://github.com/jashkenas/coffee-script/pull/1408
Addendum: Of course, a more efficient alternative than any of the above would be to write
for element, index in $('#my-table>tr')
...
which would also solve your this
problem.
Solution 3:
Your code...
class foo
@bar = 'bob loblaw'
processRows: ->
$("#my-table>tr").each ->
id = $(this).attr("id")
@processRow id
processRow: (id) ->
console.log @bar + id
Is transpiled to...
var foo;
foo = (function() {
function foo() {}
foo.bar = 'bob loblaw';
foo.prototype.processRows = function() {
return $("#my-table>tr").each(function() {
var id;
id = $(this).attr("id");
return this.processRow(id);
});
};
foo.prototype.processRow = function(id) {
return console.log(this.bar + id);
};
return foo;
})();
Which has assumed much about about the current context that it is translating to. Unfortunately, since jQuery manages context, you'll have to be explicit or declare a reference to your class's this
.
Incidentally, there are other issues with that generated code, take a look at this reduced case:
class foo
@bar = 'bob loblaw'
getBar: () ->
@bar
Transpiles to:
var foo;
foo = (function() {
function foo() {}
foo.bar = 'bob loblaw';
foo.prototype.getBar = function() {
return this.bar;
};
return foo;
})();
The results of attempting to use this piece of code:
> foo.bar;
"bob loblaw"
> var f = new foo();
undefined
> f.getBar();
undefined
Your code seems to expect that @bar
is an own property, but it's being created as a static property of the foo
function