software, programming, functional
home mail me! RSS (2.0) feed

Prototype arrays – improving upon Ajax?

There is this popular wrapper for common JavaScript idioms, called Prototype. I will not explain it - since that is done elsewhere - nor praise it. What I will do is to look critically at one part of this library, the array extensions.

This library is used by most fancy AJAX libraries popping up, whether they deal RPC or UI aspects. For two very popular examples, look at script.aculo.us and dojo.

I highly recommend one of the Pragmatic series of books to learn more about AJAX and the aforementioned wrappers: Pragmatic Ajax: A Web 2.0 Primer - a great introduction to modern JavaScript development and to Prototype in special.

Pragmatic Ajax: A Web 2.0 Primer

One of the hyped aspects of Prototype is that it wraps JavaScript arrays in Ruby-like objects. The upside is that one can use a Rubyesque each method, like in

JAVASCRIPT:
  1. function extractTokens(commaList, tokenList)
  2. {
  3.   $(tokenList).innerHTML = "";
  4.   var tokens = $F(commaList).split(',');
  5.   tokens.each (
  6.     function (token) {
  7.       new Insertion.Bottom(tokenList, "<li>" +
  8.         token + "</li>");
  9.     }
  10.   );
  11. }

which works together with the following HTML

HTML4STRICT:
  1. <head><title>Testing Prototype Arrays</title></head>
  2. <script type="text/javascript"
  3.   src="prototype.js"></script>
  4. <script type="text/javascript"
  5.   src="test_proto_arrays.js"></script>
  6. Enter a CSV list: <input type="text" id="input"></input>
  7.   onclick="extractTokens('input', 'tokens')">
  8. Extract Tokens
  9. </button>
  10. <br/>
  11. The tokens:
  12. <ul id="tokens">
  13. </ul>
  14. </body>
  15. </html>

Writing 1,2,bo we get the list of those tokens in li elements.

Beside this iterating each method, Prototype also extend arrays with methods like collect, all and detect.

Ruby Developers: yep, these are all your old Enumerable methods. The resemblance is no coincidence, since the author is a Rubyan and the library was created primarily for use from Ruby-generated client side pages.

Prototype adds these methods via a mixin called Enumerable.

So, we get the Enumerable power of Ruby! Is that not great?

Yes and no.

JavaScript exposes all objects as hashes from property name to its value, syntactically just like array access. Thus, methods names become keys in that hash.

Lua Developers: all JavaScript objects are essentially "tables".

Also remember (or learn here and now...) that arrays in JavaScript are always associative, so you can store any value as a key, not only integral indices.

For instance, in JavaScript one can do

JAVASCRIPT:
  1. // rate languages from 1 to 10 in terms of expressivity
  2. var languageRate = new Array();
  3. languageRate["java"] = 4;
  4. languageRate["c++"] = 8;
  5. languageRate["d"] = 9;
  6. languageRate["c# 1.0"] = 5;
  7. languageRate["c# 2.0"] = 6;
  8. languageRate["c# 3.0"] = 7;
  9. languageRate["haskell"] = 8;
  10. languageRate["scala"] = 9;
  11. var out = "<ul>";
  12. for (lang in languageRate) {
  13.   out = out + "<li>rate of " + lang + " is " +
  14.     languageRate[lang];
  15. }
  16. out = out + "</ul>";
  17. document.getElementById("languages").innerHTML = out;

and we hold on with the 10 till I invent that Ultimate Language ;-)

You can see that the traversal of an associative array is similar to what most modern languages provide, like the foreach in C#, or the alternative for in Java 5.0.

So, adding Prototype must make this picture even brighter, right? No. Prototype ignores this associative use of arrays in JavaScript in two distinct ways:

  1. the added methods deal only with integral keys, i.e., indexed arrays, so you cannot reach other keys in the array
  2. by adding methods to an object, those method names become keys of the array!

The second ignorance is particularly worrisome since not only do we get a lot of unwanted keys in that associative array, but these method names might conflict.

An example of this mix of methods and keys, when using associative arrays follows, now with Prototype loaded and ready.

JAVASCRIPT:
  1. var map = new Array();
  2. map[2] = "two";
  3. map["two"] = 2;
  4. // The Prototype way
  5. map.each (function (value, index) {
  6.   new Insertion.Bottom('output1', "<li>" + index +
  7.     " = " + value + "</li>");
  8. });
  9. // The JavaScript way
  10. for (index in map) {
  11.   new Insertion.Bottom('output2', "<li>" + index +
  12.     " = " + value + "</li>");
  13. }

The output1 element will have one entry:2 = two. So, we see that the Prototype enumerable methods only care about integral indices.

The output2 element will have a lot of entries... Not only our expected 2 = two and two = 2 but a lot of key-value pairs we did not add! Those are, obviously, the methods added to Array by Prototype, which includes indexOf etc. A side note is that we do not see collect and those other nifty iterating methods; the reason for these omissions is that they happen to be in the Enumerable mixin class.

Are we forced to stop using either associative arrays or Prototype? Not really, but close ;-) What Prototype does provide us is a simple step back to old JavaScript arrays by the toArray method. That returned array is devoid of those extra keys. A simple old-fashioned (associative) array.

This omission of associative array support on the part of the Prototype author is almost forcing me to sit down and write my own Ultimate JavaScript Wrapper :-) The only slight problem is that it is hard to extend anything in JavaScript without getting those stinky keys in there :-|

For other criticial views on Prototype, go to this blog.

Leave a Comment

You must be logged in to post a comment.