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

So far we haven't seen much code apart from that one line. Instead of boring you even more with yadda yadda, let's dig into some brief examples. I won't go through the whole specification. I will rather cover some features that are 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 imperative style 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.

Globals

Next I'll show you how to leak globals. Consider the example below. Can you see what changed? Why is the example leaking?

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

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

    return ret;
}

Even though the change was quite innocent now we have a leak in our hands. len is polluting our global namespace. All we did was to forget to declare our len as a var.

Many consider this a design flaw of JavaScript and I would agree. It is a good idea to declare which variables you are going to use at the beginning of the function. There are various opinions on how to achieve this syntactically but the above (apart from the bug) is what I like to stick with.

In browser environment the global namespace is also known as window for some curious reason. As this might not be the case in other environments I'm using the more general term here. This is also why it is not that nice to depend on window.

Partial Application

Let's say we have a bunch of forenames (say ['Joe', 'Jack', 'Jill']). For a sake of example we will want to suffix those with a surname. As we are being really original here, let's pick 'Johnson' as that isn't generic at all. How could we achieve that using JavaScript? You can go and code something now. I'll wait here for a while.

I hope you have something together now. Even if you don't, let's consider the problem a while. This sounds like a map. So it must be a map. Now how to define that. We'll, just look above. You can find the definition there. In case we are feeling particularly lazy and happen to have the right browser at our disposal, the snippet below will do the trick:

['Joe', 'Jack', 'Jill'].map(function(n) {return n + ' Johnson';});

Yeah, that's simple but not very over-engineered yet. How can we make our intent clearer? What if we wanted to apply the same function to some other list of items? As this is a contrived example anyway, let's try some partial application.

partial application is a way to specialize functions. In case you know how Object Oriented Programming and inheritance works in principle, you can consider this an analog to inheritance. We are simply taking something generic and making it more specific.

Instead of map I will want to end up with johnsonMap in this case. It will take a list of names and return a list of johnsoned names. Simple as that. As it happens ES4 provided us the perfect function for this purpose. It is also known as bind. There are shims available for less fortunate browsers.

The way it works is very simple. As you might guess from its name, it binds the values of function arguments to those given. In our case we can end up with something like this given we use the map definition above. This should explain why I prefer to have that callback argument as the first.

var johnsonMap = map.bind(undefined, function(n) {return n + ' Johnson';});

johnsonMap(['Joe', 'Jack', 'Jill']); // should yield the same as above

What's up with that first parameter? This brings us one of the perhaps confusing aspects of JavaScript. This concept is also known as this. Confused yet?

Sometimes you might want to bind console.log. Perhaps a bit counterintuitively just console.log.bind(undefined, 'prefix: ') won't work. Instead you will need to provide console as context like this: console.log.bind(console, 'prefix: ').

What is this?

this refers to function context. What is this context then? It depends. In this case we may set it explicitly. This means we may access contents of this Object within the context itself.

As I fear I may have confused you even more with this explanation, let's consider something more concrete. If you are as unlucky not to know what an Object is, let's stick an Object to our example. In computer science terms JavaScript Object can be considered a map of key-value pairs. All keys are always Strings whereas values can be pretty much anything.

var obj = {
    ctx: function() {console.log(this)},
    name: 'demo object'
};

obj.ctx(); // -> ???

Can you guess what object.ctx() prints out? That's right. We should see the Object itself there. Handy isn't it?

this can actually be fairly dangerous at times. It is a good idea to think through whether or not you really need it. Some libraries, such as jQuery, tend to rely on it somewhat. I don't consider that good design personally as you are effectively trading off some composability. And the last thing I want to do is to call certain functions using either call or apply speaking of which that is probably what I should cover next.

Understanding call and apply

These two might remind you of that beautiful bind. As it happens these three are siblings. call is the ugly one and I tend to favor apply over it. These two functions allow you to... wait for it... call functions. What's the point you might ask?

Suppose you are writing a test runner. You might have some kind of a function definition for the tests. It could be an Object containing name, test function pairs for instance. You will need some way to run them. This is where these guys I just introduced you to come in.

The difference between the nice apply and the less nice call is that the former accepts the parameters within an array while the latter just accepts them. It reminds me of bind this way. Both take a context parameter first and then do their thing after.

As I don't feel like writing an entire test runner here, I'll leave it as an exercise to the reader. Just to make everything clear, consider the following apply of console.log.

console.log.apply(console, ['you da js champ!']);

You can probably guess what happens there. If not, it's still not too late to change the career.

Arguments Are Fun

It is definitely fun to argue. Oh wait, I was supposed to introduce you to arguments. Not arguing with people. Wrong book.

There are generally put three ways to deal with function parameters in JavaScript. And no, I won't have an argument with you over is it a parameter of an argument. If you look from the outside, it's a parameter. If from the inside, it's an argument. Capiche?

The first way is the one we have used a lot already. You simply pass the parameters in a specific order and the function gets those as arguments in that specific order. Nothing complicated there.

Unfortunately JavaScript doesn't provide named parameters for us yet. As it happens it is possible to work around this problem with a little bit of Object magic. There is something about Object.

The third way has to do with the keyword arguments. You can probably guess what that contains. It looks like an Array but isn't quite like one.

As you might be mighty confused right now about what the guy is blabbering about, I've concocted amazing functions performing additions in various interesting and imaginative ways.

var add = function(a, b) {
    return a + b;
}

var addNamed = function(o) {
    return o.a + o.b;
}

var addMany = function() {
    var ret = 0;
    var i, len;

    for(i = 0, len = arguments.length; i < len; i++) {
        ret += arguments[i];
    }

    return ret;
}

I generally favor the first variant in my APIs. If the amount of parameters goes beyond three, I start to question myself even more than usually. That is usually a sign that something needs to change. I use the second variant particularly for jQuery plugins. In that case I often merge the passed parameters over some default ones using $.extend. The last one is preserved for special cases only.

Even though arguments might look like an Array, it isn't one. As MDN suggests, you can convert it to one either by using Array.prototype.slice.call(arguments) or Array.slice(arguments). You can shim latter in.

I hope this settles the argument once and for all. That Array.prototype actually looks a bit interesting. What's that about?

Prototypes

TODO

Factories

property example (get/set using function)

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

Generators Revisited

Generator example.

Conclusion

TBD

Clone this wiki locally