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