Functional Programming

Higher-order Functions

Let's say that you were allergic to for loops--you don't like the syntax, maybe, or you dislike having to track a separate variable just for loop position. Could we write a function that does looping for us? First, let's write a function that just adds one to every item in an array that we pass in as the first argument. var loop = function(array) { for (var i = 0; i < array.length; i++) { array[i] = array[i] + 1; } } That works for one particular loop, but we don't want to have to write a new function for every loop--that defeats the purpose. Instead, we want a general purpose function, where we can "fill in" the operation on each item.

To solve this problem, remember that functions in JavaScript are also values, just like objects, numbers, strings, and booleans. They can be passed as arguments to a function--in fact, we've done so many times, every time we've added an event listener to an object. Here's an updated version of our loop function, but this time it will accept a function as input and call that function on each item in the array. var loop = function(array, func) { for (var i = 0; i < array.length; i++) { array[i] = func(array[i], i); } }

Inside the loop, we're calling func with two arguments: the item value, and its position in the array. To use our new loop function, we just have to call it and pass in a function that will accept those two values, while returning the new value. Here's our add function again, rewritten into our generic loop form. loop(function(item, i) { return item + 1; }); Tada! We've written a loop, but there's not a for in sight. Instead, it is isolated away inside of loop. Effectively, the function we pass in becomes the inner section of the loop.

In this case, loop is what we call a higher-order function--it's a function that either operates on or returns other functions. The style of using functions this way is called (appropriately enough) functional programming, and it's extremely useful for several reasons. It helps to isolate variables, creates reusable units of operation, and (for some people) is less "noisy" and more readable than the original loop form.

The Usual Suspects

The function we've made above is more commonly known as an "each" or "forEach" operation. In most browsers (including IE9 and up), arrays have a forEach built in, like so: list.forEach(function(item) { return item + 1; }); In addition to forEach, there are a number of other common functional operations that come built-in for working with arrays. The following list includes a short description of each, as well as a small example of use.

Browser Compatibility

Rarely, of course, do most people use all these functions. The most common functional operations by far are each, map, and filter. Of course, if you're in an older browser like IE8 or below, you don't have even those available. Luckily, jQuery provides its own versions of them, albeit slower and with a few quirks.

$.each() can be called on both objects and arrays, unlike the built-in function which is only defined for arrays. It also swaps the order of arguments, with the index coming first and the item value second (these are different from the built-ins, because jQuery actually added its each before the browsers did). Because each is defined on the jQuery object, and not on arrays, you need to pass in the array you want to loop over as the first argument. $.each(["A", "B", "C"], function(i, item) { console.log(i, item); //logs out the index of each item, followed by its value });

jQuery's map function doubles as its version of filter. If you return null from the input function, that item will be removed from the resulting array--this is a handy property that would actually be nice in the regular map function. Like $.each, $.map also works on both arrays and objects (although it only returns arrays), and must take its target as the first argument, followed by the actual function that will do the mapping/filtering. var pets = { dog: "Wallace", cat: "Neko", hamster: "Nadia" } var startWithN = $.map(pets, function(prop, name) { var letter = /^N/; //find names that start with N if (letter.test(name)) { return name; } else { return null; //filter out non-N names } }); //startWithN = ["Neko", "Nadia"];

Between these two functions, you have most of the necessary tools for working with lists using functions. If you find that you want more functional power, but you need to be compatible with older browsers, you may want to check out Underscore.js, a library that includes not just each, map, reduce, and filter, but a dizzying arry of other functions for working with arrays.

Call (and Apply) Me Maybe

Once you get used to the idea that functions are just values to be passed around and called by other functions, it opens up an enormous range of potential applications. In fact, most of the powerful ideas in JavaScript come from the flexibility of these first-class functions. For example, let's say that you wanted to be able to delay the execution of a function. You could call setTimeout every time, but that gets cumbersome. Instead, let's write a function that wraps our original function in a delay of our choosing. var wait = function(f, delay) { return function() { setTimeout(f, delay); }; }; //now, we can create a delayed version easily: var logHello = function() { console.log("Hello"); }; var delayedHello = wait(logHello, 1000); //call the returned function delayedHello(); //after a second, you'll see it say "Hello"

A more common scenario is that you'd want to throttle function calls, say inside a scroll listener that's called many times per second. You only want to run your function once every half-second, no matter how many times it's called. That's an easy functional order: var throttle = function(f, delay) { var snooze = false; return function() { if (snooze) { //we're sleeping, do nothing return; } //otherwise, run our function and set snooze temporarily f(); snooze = true; setTimout(function() { //after a delay, wake back up snooze = false; }, delay); } }

In both of these cases, however, we've called our function directly. Functions called this way, you may remember, have a this value of either the window object, or undefined. The same thing happens when we use setTimeout--because the function value is passed in, but it's not called in the context of its parent object, our desired value of this won't be preserved. However, we can force JavaScript to use a given this value by running functions with call() and apply() instead of direct invocation. var logThis = function() { console.log(this); } logThis(); //either window or undefined //by using call(), we can set the this value logThis.call(document); //logs out the document object logThis.call(logThis); //logs out itself

Both call and apply are methods that are attached to every JavaScript function. As their first argument, they take the context object that you'd like to be the value of this for the function. Their difference comes from what they expect next: call will pass its next arguments in as arguments to the function, while apply only accepts an array as its second argument, and will expand those into the called function's argument list. var logArgs = function(a, b, c) { console.log(this); console.log(a, b, c); }; logArgs.call(document, 1, 2, 3); //logs out the document, followed by 1, 2, 3 logArgs.apply(jQuery, [4, 5, 6]); //logs out jQuery, followed by 4, 5, 6

Although call is more straightforward (and a little faster), apply actually enables a couple of useful tricks. For one thing, inside each function, its arguments are available as an array-like object named (of course) arguments. By passing this directly into apply, you can bind a function to a new permanent context, but still allow it to take arguments like normal: var bind = function(f, context) { return function() { f.apply(context, arguments); } }; var bound = bind(function(a, b, c) { console.log(this); console.log(a, b, c); }, document); bound(1, 2, 3); //logs document for the value of this, not window //but also still logs out 1, 2, 3 as normal

Bound functions can be very useful for event listeners or object methods. If called directly or through a timeout, those methods would no longer know about their parent objects unless they're bound to its context. This is so handy, the same browsers that support functional operations like map also support bind as a third method on all functions alongside call and apply.