JavaScript 1.8 Progress


In case you haven’t been following the progress being made on JavaScript 1.8 here’s a quick overview of what’s already landed in the latest Firefox 3 nightlies.

So far, three main features have landed, with a few more to come. In general, this release will be relatively ‘light’, mostly attempting to bring the current JavaScript implementation closer to the desired JavaScript 2 spec.

Expression Closures

This just landed in trunk and I’m sure it’ll tickle those of you who enjoy functional programming. Effectively, this addition is nothing more than a shorthand for writing simple functions, giving the language something similar to a typical Lambda notation.

For example, look at the Lambda notation that’s available in a couple languages, in comparision to JavaScript 1.8.

Python:

  1. lambda x: x * x

Smalltalk:

  1. [ :x | x * x ]

JavaScript 1.8:

  1. function(x) x * x

(For reference) JavaScript 1.7 and older:

  1. function(x) { return x * x; }

I think, probably, my favorite use for this shorthand will be in binding event listeners, like so:

  1. document.addEventListener("click", function() false, true);

Or combining this notation with some of the array functions from JavaScript 1.6:

  1. elems.some(function(elem) elem.type == "text");

This will give you some JS/DOM code that looks downright elegant.

Generator Expressions

This is another one that was just committed to trunk. It’s a little bit more involved than the previous addition, as this one encompasses a number of concepts. Specifically, it requires knowledge of most of the features within JavaScript 1.7, specifically Iterators, Generators, and Array comprehension. This particular feature is based off of the generator expressions that exist in Python.

In the ticket tracking this feature, Brendan posted an elegant, functional, Sudoku solver written using the new syntax that this addition affords us. This demo is based off of a similar one written in Python used to demonstrate its generator expressions.

To better understand what this feature means, lets look at a single line of JavaScript 1.8 code, taken from the Sudoku solver.

  1. dict([s, [u for (u in unitlist) if (u.contains(s))]] for (s in squares))

This line relies upon the dict() function, which takes a 2xN sized matrix, and converts it into a key/value pair object. The code of which can be found here:

  1. function dict(A) {
  2.     let d = {}
  3.     for (let e in A)
  4.         d[e[0]] = e[1]
  5.     return d
  6. }

Let’s go through that line of code, part-by-part, to better understand what exactly is going on.

  1. [u for (u in unitlist) if (u.contains(s))]

The first part of the line is an example of array comprehension coming from JavaScript 1.7. Specifically, we’re iterating over ‘unitlist’ and building it out into an array of keys (excluding which keys don’t contain ‘s’).

  1. [s, ...] for (s in squares)

The second part of this line is another example of array comprehension. At first glance, it appears as if it’s another feature from JavaScript 1.7, a destructuring assignment, although, that isn’t the case. A normal destructuring assignment only occurs when you’re assigning a value, however in this situation we’re just creating an array value using array comprehension. These new 2-unit arrays will then plug back into the dict function.

  1. dict([s, ...] for (s in squares))

This is where the magic happens. In JavaScript 1.7 we could’ve called the dict() function like so:

  1. dict([[s, ...] for (s in squares)])

Note the extra, explicit, use of array comprehension. The issue with that extra comprehension is that it has to be completely run when it’s first encountered, in order to build out the full array (which will then be turned into a ‘dictionary’). However, the lack of extra [...] is what makes this a generator expression. That makes that line in JavaScript 1.8 equivalent to the following in JavaScript 1.7:

  1. dict((function(){ for (s in squares) yield [s, ...] ; })())

As you’ll see, it the generator expression lazy-builds the resulting array, handling it as a generator – meaning that the specific values won’t have to be generated until they are explicitly needed by the dict() function (resulting in less loops and better overall performance).

Here’s another example of a generator expression

  1. // Create a generator that loops over an object values
  2. function val_iter(obj) {
  3.     return (obj[x] for (x in obj));
  4. }
  5.  
  6. // Iterate through an objects keys
  7. for ( let key in obj ) { ... }
  8.  
  9. // Iterate through an objects values
  10. for ( let value in val_iter(obj) ) { ... }

Of course, the val_iter() function could be built with JavaScript 1.7 right now, using yield:

  1. function val_iter(obj) {
  2.     for (let x in obj)
  3.         yield obj[x];
  4. }

Most likely, though, generator expressions will see the most use in memory/cpu hungry code (like the Sudoku solver), since solutions will now be able to get results when they need them, as opposed to loading them all up front.

Fun with iterators

On a side note, I’ve been playing with iterators/generators a lot more lately and one particular feature that I’ve wanted was the ability to easily iterate over a set of numbers. (e.g. 0 – 9) With a little bit of dancing, we can add that functionality to the language, like so:

  1. // Add an iterator to all numbers
  2. Number.prototype.__iterator__ = function() {
  3.     for ( let i = 0; i < this; i++ )
  4.         yield i;
  5. };
  6.  
  7. // Spit out three alerts
  8. for ( let i in 3 ) alert( i );
  9.  
  10. // Create a 100-unit array, filled with zeros
  11. [ 0 for ( i in 100 ) ]
  12.  
  13. // Create a 10-by-10 identity matrix
  14. [[ i == j ? 1 : 0 for ( i in 10 ) ] for ( j in 10 )]

This may be old hat for some, but I’ve gotten a real kick out of it.

Array Reduce

The last feature to sneak in recently was the addition of the missing Array.reduce/Array.prototype.reduce function, from the JavaScript 1.6 Array Extras.

You would call reduce on an array, something like this:

  1. someArray.reduce( fn [, initial] );

with the function being called like so:

  1. someArray.reduce(function(lastValue, curValue){
  2.   return lastValue + curValue;
  3. });

The “lastValue” argument is the return value from the previous call to the reduce callback function. The first time the callback is called, it’ll have a “lastValue” of the first item in the array (or an initial value, if you’re provided it in the call to the reduce function) and a “curValue” of the second item in the array.

Thus, if you wanted to sum up the numbers 0-99, you could do it like so (using JavaScript 1.8, and the number iterator from above):

  1. [x for ( x in 100 )].reduce(function(a,b) a+b);

Pretty slick!

You could also use the reduce function to do things like “merge sets of DOM nodes into a single array”, like so (thanks Andrew!):

  1. nodes.reduce(function(a,b) a.concat(b.childNodes), []);

Try it yourself

Everything that I’ve mentioned above is live today in the latest nightly builds of Firefox 3, thus if you want to try some of the above for yourself, just do the following:

  1. Download a Nightly of Firefox 3
  2. Create a page that has the following script tag in it (which was just committed):
    1. <script type="application/javascript;version=1.8"> ... your code ... </script>

And that should be all that you need – enjoy!

Posted: May 30th, 2007


If you particularly enjoy my work, I appreciate donations given with Gittip.

37 Comments (Show Comments)



Comments are closed.
Comments are automatically turned off two weeks after the original post. If you have a question concerning the content of this post, please feel free to contact me.


Secrets of the JavaScript Ninja

Secrets of the JS Ninja

Secret techniques of top JavaScript programmers. Published by Manning.

Ukiyo-e Database and Search

Ukiyo-e.org

Japanese woodblock print database and search engine.


John Resig Twitter Updates

@jeresig

Infrequent, short, updates and links.