Along the way, though, I’ve been hearing an increasingly strong chorus in favor of jQuery. I had taken a cursory look at jQuery and was impressed, but didn’t see anything compelling enough to make me switch. But increasingly the voices favoring jQuery have included people I really respect, including (over the past few months) several of my colleagues here at Relevance.
And here’s the capsule summary: jQuery is a very nice piece of work, and makes some common tasks easier than their Prototype equivalents. Where it’s good, it’s very good indeed. But its design is uneven, and its scope is limited. For me, at least, Prototype is still the tool of choice. I think it’s a richer, more thorough, and overall better designed library.
I don’t intend this to be an anti-jQuery screed (although I will rant a little about a couple of things that seem particularly poorly designed). Neither is it an exhaustive comparison of Prototype and jQuery. Simply put: praise has been lopsided in favor of jQuery of late, and I feel like singing the praises of my personal favorite.
Note: in this post I’ll be showing code examples using both Prototype and jQuery. To keep things straight, I’ll color-code the listings. Prototype samples will have a green background:
Samples using jQuery will have a yellow background:
I would describe the “three pillars” of jQuery this way:
Each of those are powerful features, but more importantly, they work beautifully together. They allow jQuery programmers to express complex manipulations of the DOM in very concise ways, without losing clarity.
This section isn't very long, so someone skimming this post might get the idea that I don't think jQuery has a lot going for it. But I really admire this aspect of jQuery. The coding style that arises from these features shapes the way you do everything in jQuery. Furthermore, the fact that three such simple ideas can have such enormous positive effects on code written using the library speaks volumes for the power of good design.
It's just a shame that the rest of jQuery is not as well designed.
In some ways, it's harder to make the case for Prototype, because that case depends on numerous small details, some of which are strengths of Prototype, and some of which are weaknesses of jQuery.
However, I have to say that, while jQuery wins in this area, it doesn't win by as much as some would have you believe. Most of the comparisons I've found on the web are rather ridiculous, because they compare jQuery against very poor examples of Prototype usage. Take this example:
There’s a much better way to do that in Prototype:
invoke method in Prototype will apply a named method to each element of a collection.
Prototype's DOM methods are also chainable, even when using
Critics of Prototype have tended to focus on three issues: code size, performance, and namespace pollution.
These days, Prototype and jQuery have about the same code size. Prototype is a little larger, especially since it doesn’t ship with a minimized version. But if you care about code size, that’s easily remedied. It’s not hard to run a JS minimizer. It’s also worth pointing out that the very small code size gap is not gratuitous; Prototype has more functionality. (In looking at this issue, I’ve compared core Prototype against core jQuery, and also Prototype+Scriptaculous against jQuery+jQuery.ui.)
Although Prototype’s performance has not always been good, it has improved a great deal, in many cases by learning lessons from how jQuery has done things (and in some cases, the patches were even submitted by the jQuery developers). Today, Prototype’s performance is close to that of jQuery, and the very poor performance Prototype used to have for some situations (and in some browsers) has been eliminated.
Now I’ll describe where I think Prototype has the advantage, touching on three different areas: a more polished API, some issues with the way jQuery uses
this, and a broader, more versatile array of programming facilities.
I’ve already mentioned how beautifully designed jQuery’s core is. The problem is that the parts of jQuery outside that core are often poorly designed, in ways that really frustrate me. I’ll describe four.
When using the
each method for iteration, the arguments to the iteration function are in the wrong order. (I know, I know … if you program the “jQuery way” you won’t actually use those arguments. I’ll address that later.) Here’s an example:
When iterating over a collection, you nearly always want to work with the element of the collection, and you rarely need to actually refer to the index. The arguments should be the other way around, so that you could omit the
index argument in the vast majority of cases where you don’t need it. Here’s the equivalent in Prototype:
each does pass an index argument to the iteration function, but it’s the second argument, so it can be omitted if not used—which is nearly always.
But jQuery can’t even be consistent about this. Here’s how jQuery’s
The argument order for
index—just what you want, but it’s sad that it’s inconsistent with
map is broken in other ways as well … the traditional definition is that the result array has the same number of elements as the original, but with jQuery’s version that’s not true. (What I refer to as “the traditional definition” of
map is the one used by Common Lisp, Scheme, Haskell, OCaml, Erlang, Ruby, Python, Perl, Scala, and more.) I won’t dwell on the details of how jQuery’s
map is broken, but one consequence is that it’s impossible to use it to produce an array of arrays, because any arrays returned from the mapping function will be flattened into the result array.
jQuery has a
unique method. It returns all the unique elements from an array; that is, it eliminates the duplicates. Very handy. But for some reason, it only works on arrays of DOM elements. It silently fails (by returning a copy of the original array) if you pass it an array of anything else. This is a known problem, but the bug was “fixed” by just updating the documentation to make the behavior clearer. How difficult would it have been to make it work correctly? (Answer: not very, because the bug report came with a patch and tests.) Prototype’s
uniq method works in both cases.
Finally: jQuery also has an
inArray method. You can call it like this:
But what does it return? Well, “in array” is a question that has a yes or no answer, so I’m guessing a boolean. Let’s see what the docs say:
Determine the index of the first parameter in the Array …
false can be used where a boolean is expected. Truthiness is useful, because it means boolean results can actually be useful outside of strictly boolean contexts, and returning the actual index in this case is a great example of that. So as long as
undefined if the value is not in the array, we’re in good shape. But I left out part of that documentation above. The rest of it is:
… (-1 if not found).
Gack! How useless is that? The name of the method implies that it’s a boolean query, but the result value is not useful in a boolean context. And if you just try using it that way, making an assumption based on the name, you’ll have fun debugging it, because there won’t be an error; it’ll just always be true.
For fifty years, programming languages have been using two common names for the job that
position. Having a method called
inArray that works like this is just sloppy library design.
When complaining about iterator arguments to jQuery’s
each, above, I mentioned that the “jQuery way” is not to use the arguments to the iterator function at all. That’s because when jQuery’s
each calls your iterator function, it does so in such a way that the variable
this is bound to the current item. So the idiomatic way of using
each in jQuery is:
That’s the kind of decision that makes really simple cases very easy, but only at the expense of a lot of confusion when things are a bit more complicated. That’s because
Here’s an example. I have an object (called
chart), and in a method on that object I need to iterate over an array (stored in the property
columns), generating a header for each item using
buildHeader, which is another method on
chart. Within methods of
chart, of course,
chart is referred to as
But that manual iteration sure feels clumsy; it would be nice to be able to do that with
each. Let’s do that with jQuery:
But that doesn’t work;
this within the iterator function has been rebound to the current item. The
this variable from the surrounding scope has been shadowed by a different
this that refers to the current iteration item. But unlike
this has been shadowed implicitly, by jQuery’s implementation of
The preferred jQuery way to write the example is to provide an alias for
this that doesn’t get shadowed by
But notice that
this in the iteration function is a different
this can make things very confusing.
Here’s how to do the same thing in Prototype:
this. But that’s not enough justification for the design choice jQuery makes.
each were properly designed, one could write the basic iteration idiom this way:
Would that be so bad? No; in fact, it would be a huge improvement. This style is just as easy in the simple case, and far less troublesome in more complex cases.
I mentioned earlier that jQuery has a core of functionality built around querying and manipulating DOM elements. It also has mature support for Ajax operations. Beyond that, though, jQuery doesn’t aim for very extensive facilities. (Of course there are numerous plugins and add-ons that do all kinds of things, as there are for Prototype.)
Prototype also adds useful new basic types (such as
Template, and a true
I don’t think this makes a slam-dunk case for Prototype over jQuery. And certainly jQuery could improve; its design is not fundamentally limited. (For that matter, many of what I consider the missing pieces in jQuery are available as plugins.) It’s unfortunate that some of jQuery’s API flaws can’t be fixed without breaking compatibility, but in a future version even that may be an option.
(Thanks to Stuart Halloway, Chad Humphries, Larry Karnowski, Adam Keys, and Jason Rudolph for their comments—in some cases pro, in others con—on a draft of this article.)