Skip to content

JavaScript High Order Functions

tdkehoe edited this page Jun 2, 2015 · 29 revisions

A function that takes another function as an argument is a high-order function.

Suppose you are a coder in North Korea. Your job is to put a banner across every webpage extolling the greatness of the Supreme Leader. You could write this function:

var longLiveKimJongUn = function() {
    console.log("Long Live Kim Jong-un!");
}

Whenever the function is called, the banner is printed.

But you have an idea. You set up a query on the location of the user. If the user is in North Korea the banner says Long Live Kim Jong-un!" But if the user is in the free world the banner says "Help! I'm being held prisoner in North Korea and forced to write JavaScript!"

var longLiveKimJongUn = function(callback) {
    callback();
}

var locationNorthKorea = function() {
    console.log("Long Live Kim Jong-un!");
}

var locationFreeWorld = function() {
    console.log("Help! I'm being held prisoner in North Korea and forced to write JavaScript!");
}

longLiveKimJongUn(locationNorthKorea);

The function longLiveKimJongUn takes the location query as its parameter. The function runs callback, which selects which function to run (depending on the user's location). The nested function then prints the banner.

#Creating a forEach() Iterator

To save time writing iterators you create a function:

var forEach = function(arr, callback) {
    for (var i = 0; i < arr.length; i++) {
        callback(arr[i]);
    }
};

var arr =[1,2,3,4];

forEach(arr, function(item) {
    console.log(item + 10);
});

//11, 12, 13, 14

To run forEach() you must include as parameters an array arr and a function. In the forEach function the callback parameter is replaced with the anonymous inner function, and the anonymous function's parameter item is replaced with each element arr[i].

This program is useful for seeing the results but it doesn't return an array so the results are difficult to use. The map() method (below) is similar to forEach() but returns an array. This forEach() program can't pass out results because it has no return statements. The results are passed out of the functions because the anonymous function includes console.log. callback(arr[i]) is always undefined.

#Creating a map() Iterator

 var map = function(arr, callback) {
    var results = [];
    for (var i = 0; i < arr.length; i++) {
        results.push(callback(arr[i]));
    }
    return results;
};

var arr = [1,2,3,4];

console.log(map (arr, function(item) { return item * 10 }) );

map() differs from forEach() in that it creates a second array with the results.

First, we need to declare the second array (results).

Next, callback symbolizes the second (nested) function. The nested function is anonymous. You can think of its name as callback. In this case the anonymous function is item * 10, so callback becomes item * 10.

item is a parameter of the anonymous function. In the map function item is replaced by _arr[i].

Then we push() the original elements, one by one, as modified by the callback function into the new array results.

Then the array results are returned.

Note the second return. There are two functions so two returns are needed. The second return assigns the value of callback(arr[i]) to function(item) {return item * 10;}. In other words, the new value (ten times the old value) replaces the anonymous function.

#Creating a filter() Iterator

var filter = function(arr, callback) {
    var results = [];
    for (var i = 0; i < arr.length; i++) {
        if (callback(arr[i]) === true) {results.push(arr[i])};
    }
    return results;
};

var arr = [5,10,15,20];

console.log(filter (arr, function(item) { return item > 10 }) );

This tests is each element of the array is greater than ten, which returns a boolean (true or false). If the value is true then the element is pushed to the second array results.

if (callback(arr[i]) === true) can also be written if (callback(arr[i])) (but is less readable).

#Creating a reduce() Iterator

var reduce = function(arr, callback, start) {
  var reduced = 0;

  for (var i = start; i < arr.length; i++) {
     reduced += callback(arr[i-1], arr[i]);
  }
  return reduced;
};

// var arr = ["this ", "is ", "a ", "string"];

var arr = [1,2,3,4];

console.log(reduce (arr, function(prev, cur) { return prev + cur; }, 0) );

This adds all the elements of an array:

[1,2,3,4] --> 10
["this ", "is ", "a ", "string"] --> this is a string

The start (third) parameter sets the element on which the iterator starts. 1 is the default. 0 doesn't work because arr[-1] doesn't exist.

The program doesn't work perfectly with strings because 0 is concatenated to the start of the string. This could be fixed by checking for type and setting reduced as null for strings. There might be a better way to solve this problem.

The += operator adds each pass to the previous passes. However, this operator doesn't work if the variable reduced starts as null or undefined. Setting this variable at 0 fixes this problem.

Clone this wiki locally