Learning JavaScript – a malleable language

Learning that it’s extendable

I recently read Crockfords classic: JavaScript, the Good Parts. Many thanks, Peter for recommending it! The books preface states that JavaScript is Lisp in C clothing. This C clothing is what makes people underestimate js: well, yet another C-like scripting language. Of course, it isn’t. In this book he explains how js is a functional language since functions are first-class citizens. Among other things he shows several examples on how to extend js with your favourite functional constructs.

Say for instance, he adds code like this:

Function.prototype.method = function (name, func) {
  this.prototype[name] = func;
  return this;
};

What did we just see here?

Crockford here augments the Function prototype with a function called method.

For the record, ‘augment’ is a fancy word for ‘extend’. Unfortunately the word ‘extend’ has special meaning in object-oriented languages. Thus js folks augment their types.

Well, could we augment the Number prototype to enable code like this?

5.times(function(){
  document.writeln('hello');
});

Actually, no. We couldn’t. But we could get close enough.

Close enough

So, this function called ‘method’ makes it easy to add functions to prototypes of other types. E.g. this is perfectly legal:

Number.method('times', function(func) {
  var i;
  for (i = 0; i < this; i++) {
    func();
  }
});

But it won’t work:

5.times(function() {
  document.writeln('hello');
});

It’ll throw a syntax error:
SyntaxError: identifier starts immediately after numeric literal

However, this will work:

5['times'](function() {
  document.writeln('hello');
});

 

Metaprogramming

What does this 5[‘times’]() tell us? It tells that we can find a function by name and invoke it as a method. Actually, we can find a property by its name; you can do various things to it; if it’s a function then of course you can invoke it. This is serious metaprogramming.

There are tons of things that one can do with javascript metaprogramming. This slideshow shows lots of examples, like self-optimizing code.

Then, we could ask, why didn’t folks wrote all the ruby-like iterators? Why didn’t they bend the language into all those directions? Basically, we could ask: why shouldn’t we augment the types whenever we can?

Why not augment everything?

If you augment common types like Number or the DOM then you’ll have problems with clashing names, consistency and being future-proof.

Name clashing is a problem when you want to integrate different projects into a bigger one but they have different things under the same name. Being future-proof is a special case of this: the browsers can augment the DOM objects with their new functions as well as ECMAScript-12345 might augment their Number prototype with a new times() function.

Consistency can be a problem too, when some of your Number functions use snake_case while others use camelCase. This would be a great source of errors.

As once @pbakondy explained to me, this is how prototypejs works. And this is why jQuery and its wrapper class is more popular.

The usual functional constructs

Let’s do it with one hand behind our back: let’s not use yield because it’s still coming soon.

Using Mozillas now deprecated iterator API we could write code like this:

var it = Iterator([1, 2, 3, 4, 5]);
var doubles = (i * 2 for (i in it));
for (i in doules) {
  document.writeln(i + ' '); // writes 2 4 6 8 10
}

But we don’t need to rely on deprecated code. Dan Kogai provided us a beautiful library called js-list-lazy. It provides us all the usual functions: each(), map(), filter(), take(). Moreover, you can provide your own generators too. The documentation page of this package shows how to create a memoized fibonacci generator:

var fib = {
    0:0,
    1:1,
    n:1,
    get:function(n) {
        if (n in this) return this[n];
        while(this.n < n)
            this[++this.n] = this[this.n-1] + this[this.n-2];
        return this[n];
    }
},
fb = List.Lazy(fib);
fb.get(22); // 17711
fb.take(10); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

jQuery has it’s own implementation of these methods. They provide each(), filter() and map(). You can find their detailed description here. If, for any reason it wouldn’t be enough for you, you can map that to js-list-lazy like this:

List.Lazy.fromJQuery = function(jq) {
  var adapter = {
    'get': function(n) {
      return jq.eq(n);
    },
    'length': jq.length
  };
  return List.Lazy(adapter);
};

Which you can use like this:

$(document).ready(function() {
  var ll = List.Lazy.fromJQuery($("li"));
  ll.filter(function(jq) {
    return jq.text().indexOf("Alice") > -1;
  }).each(function(jq) {
    jq.addClass('faded');
  });
});

This “any reason” here was “just for the heck of it”. Here you can find the full proof-of-concept project.

What was all that about?

It was only about learning what one can do with js. I recommend you to write your own experimental version of filter(), map() and each() – you’ll see how easy it is. Then take a look the solution of somebody else and see what they did to make it production-ready. At least, this is what I did and it taught me a lot about the language itself.

Advertisements

About tamasrev

A software developer, specialized in Java, addressing himself as generalist. A proud daddy.
This entry was posted in programming and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s