Reputation: 5026
I'm attempting to learn backbone.js and (by extension) underscore.js, and I'm having some difficulty understanding some of the conventions. While writing a simpel search filter, I thought that something like below would work:
var search_string = new RegExp(query, "i");
var results = _.filter(this, function(data){
return search_string.test(data.get("title"));
}));
But, in fact, for this to work I need to change my filter function to the following:
var search_string = new RegExp(query, "i");
var results = _(this.filter(function(data){
return search_string.test(data.get("title"));
}));
Basically, I want to understand why the second example works, while the first doesn't. Based on the documentation (http://documentcloud.github.com/underscore/#filter) I thought that the former would have worked. Or maybe this just reflects some old jQuery habits of mine... Can anyone explain this for me?
Upvotes: 3
Views: 5769
Reputation: 434685
I'd guess that you're using a browser with a native Array#filter
implementation. Try these in your console and see what happens:
[].filter.call({ a: 'b' }, function(x) { console.log(x) });
[].filter.call([1, 2], function(x) { console.log(x) });
The first one won't do anything, the second will produce 1
and 2
as output (http://jsfiddle.net/ambiguous/tkRQ3/). The problem isn't that data
is empty, the problem is that the native Array#filter
doesn't know what to do when applied to non-Array object.
All of Underscore's methods (including filter
) use the native implementations if available:
Delegates to the native filter method, if it exists.
So the Array-ish Underscore methods generally won't work as _.m(collection, ...)
unless you're using a browser that doesn't provide native implementations.
A Backbone collection is a wrapper for an array of models, the models array is in c.models
so you'd want to:
_.filter(this.models, function(data) { ... });
Backbone collections have several Underscore methods mixed in:
Backbone proxies to Underscore.js to provide 28 iteration functions on Backbone.Collection.
and one of those is filter
. These proxies apply the Underscore method to the collection's model array so c.filter(...)
is the same as _.filter(c.models, ...)
.
This mixing-in is probably what's confusing the "should I use the native method" checks that Underscore is doing:
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
You can use _.filter
on a plain old object (_.filter({a:'b'}, ...)
) and get sensible results but it fails when you _.filter(backbone_collection, ...)
because collections already have Underscore methods.
Here's a simple demo to hopefully clarify things: http://jsfiddle.net/ambiguous/FHd3Y/1/
Upvotes: 4
Reputation: 6914
For the same reason that $('#element')
works and $#element
doesn't. _
is the global variable for the underscore object just like $
is the global variable for the jQuery
object.
_()
says look in the _
object. _filter
says look for a method named _filter
.
Upvotes: -1