Reputation: 19117
I've the following two CoffeeScript class definitions. I expected them to the same behavior, but they don't. In particular accessing A
on instances of DoesNotWork
is undefined.
fields = ["A","B","C"]
class DoesNotWork
constructor: () ->
_.each(fields, (f) -> @[f] = ko.observable(''))
class DoesWork
constructor: () ->
@A = ko.observable('')
@B = ko.observable('')
@C = ko.observable('')
the above code compiles to
var DoesNotWork, DoesWork, fields;
fields = ["A", "B", "C"];
DoesNotWork = (function() {
function DoesNotWork() {
_.each(fields, function(f) {
return this[f] = ko.observable('');
});
}
return DoesNotWork;
})();
DoesWork = (function() {
function DoesWork() {
this.A = ko.observable('');
this.B = ko.observable('');
this.C = ko.observable('');
}
return DoesWork;
})();
What newbie JS subtly am I missing?
Upvotes: 3
Views: 808
Reputation: 77416
Yet another solution (arguably the most readable and efficient) is to skip _.each
and instead use CoffeeScript's for...in
iteration:
for f in fields
@[f] = ko.observable ''
You could even postfix the loop to make it a one-liner:
@[f] = ko.observable('') for f in fields
Remember that loops in CoffeeScript don't create context or affect scope; only functions do.
Upvotes: 6
Reputation: 77416
Craig's answer is correct, but an alternative solution is to define your anonymous function as a bound function. In this case, that would let you write
_.each(fields, ((f) => @[f] = ko.observable('')))
The =>
binds the function to the context in which it's defined, so that this
always means the same thing in the function no matter how it's called. It's a very useful technique for callbacks, though in the case of _.each
, it's a bit less efficient than passing this
in.
You could do the same thing using Underscore by writing
callback = _.bind ((f) -> @[f] = ko.observable('')), this
_.each(fields, callback)
but =>
saves you a lot of typing!
Upvotes: 3
Reputation: 7671
'this' in the anonymous function passed to _.each is bound to the anonymous function, not the parent object. _.each does allow passing a context object so that this will be bound properly though
http://documentcloud.github.com/underscore/#each
so pass a ref to the object you are trying to bind to in the 3rd arg of each:
class ShouldWorkNow
constructor: () ->
_.each(fields, ((f) -> @[f] = ko.observable('')),this)
Upvotes: 4