Skip to content
bebraw edited this page Apr 2, 2013 · 10 revisions

We've gotten quite far and I haven't shown any actual code yet. It's time to change that. I won't go through the whole specification. I will rather cover some features I cover essential to know and understand well.

Functions

Let's start with a simple math function that clamps given input based on given bounds. Here we go:

function clamp(a, min, max) {
    if(a < min) return min;
    if(a > max) return max;

    return a;
}

In this case I decided to use a good ol' if statement to deal with the logic. The same may be written using ternary operators perhaps familiar to some from C.

function clamp(a, min, max) {
    return a < min? min: a > max? max: a;
}

It's getting a bit convoluted alright, maybe it's not a good idea to nest those. Finally we can rewrite it using native min and max functions inherited from Java 1.0. You will find a bunch of these at Math module.

function clamp(a, min, max) {
    return Math.max(min, Math.min(max, a));
}

// we can invoke any of these three simply using
clamp(4, 10, 20); // yields 10 in this case

There are likely some other ways to achieve the same result. The purpose of these three was just to give some idea of the basic structures and show the basic function syntax.

In JavaScript functions are first class citizen unlike in perhaps some less dynamic languages. You can simply pass functions to functions. These are also known as callbacks. This type of programming is quite commonly used in event based programming.

Let's define a simple map function this way. map is something that takes a callback (operation) as input, applies that operation for each member of the given data structure and then returns the result. In this case I'll stick to arrays although it is possible to generalize this approach to work with objects too. A basic implementation could look like this:

function map(cb, arr) {
    var ret = [];

    for(var i = 0, len = arr.length; i < len; i++) {
        ret.push(cb(arr[i], i));
    }

    return ret;
}

map(function(v) {
    return v * 2;
}, [34, 2, 5]); // yields [68, 4, 10]

There are a couple of things going on here you should be aware of. Notice the loop syntax. It's quite ugly alright but it's fine to use it on lower level constructs like this.

Sometimes it may be beneficial to name the callback (function mul(v) would work in this case) to make it easier to debug the code. The debugger will be able to use this information and show the name at a stack trace.

There is actually a nice alternative for the ugly default syntax. It is know as Array.forEach. Check it out. See Array.map while at it.

Using these may require shims depending on which browsers you want to support but on the other hand they make language so much more fun to use it's usually worth it.

TBD: partial, arguments (object + defaults + {}), factories (prop example), this

Data Types

JavaScript's basic types include Object, Array, String, Number, Boolean, null and undefined. It is possible to mimic more complex types, such as queues and sets, quite easily using these. A queue of sort can be implemented using an array. We primarily need to make sure that it's length has been fixed to some predefined value. It's implementation could look like this for example:

function queue(len) {
    var ret = [];
  
    ret.push = function(a) {
        if(ret.length == len) ret.shift();
        return Array.prototype.push.apply(this, arguments);
    };
  
    return ret;
}

var a = queue(3);
a.push('cat');
a.push('dog');
a.push('chimp');
a.push('giraffe');
console.log(a); // should contain dog, chimp, giraffe now

Here we abuse the fact that even an array is an object in JavaScript. In this case we simply attach a custom version of push to our queue. It keeps track of the queuing logic. Otherwise the functionality provided by array is enough for our purposes. You could for instance pop to get and remove the last item or shift to do the same but for the first item.

One interesting aspect of this example is the usage of a closure. As you can see the inner function (push) accesses its parent. This is a very powerful feature. We can implement various patterns by utilizing it. Remember that inner functions may access the content of their parent but not vice versa. Closures can be treated as namespaces.

We can do something a bit similar with sets. In this case we benefit from the fact that all keys of an object are unique. That is essentially the definition of a set. The only limitation of this approach is that we may use only numbers or strings as keys. Often this is enough, though.

Functions Are Objects Too

To paraphrase Mitt Romney functions are objects too. This means you may attach custom properties to them. These properties in turn may be used to do plenty of cool stuff. TODO: show how annotate.js does this.

Hoist the Sails!

TBD

Prototypes

TBD

Generators Revisited

Generator example.

Clone this wiki locally