Current Analyst @Morgan Stanley. Prev Quant Research Intern @y-intercept, Summer Analyst @Morgan Stanley, Data Science Intern @Societe Generale, Student Ambassador @Microsoft. CS '22 @HKU.
If you have been programming in JavaScript for sometimes, chances are you might have used Higher-Order Functions somewhere in your code without even realizing it. Though it sounds like a complex JavaScript lingo, it really isn't that difficult to understand.
To understand what Higher-Order Functions is, let's try to take a look at what it's not. In JavaScript, regular functions are often said to be a First-Class Functions. First-Class Functions is a concept shared across programming languages when functions in a particular language are represented and treated just like any other variables. Be it primitive or reference type variable.
In JavaScript, functions are stored as if each of them is an object. They are indeed just a bunch of Function
class instances. Here are some little experiments to proof that:
function addTwoNumbers(a, b) {
return a + b;
}
const a = 1;
const b = 2;
const c = addTwoNumbers(a, b);
console.log(c); // prints 3
To make the code somewhat shorter, some people might also consider doing this:
console.log(addTwoNumbers(a, b)); // prints 3
But, what if we try console logging the function without calling it? Here is what you'll obtain:
console.log(addTwoNumbers); // prints [Function: addTwoNumbers]
What we can infer from the generated output above is that addTwoNumbers
is nothing but an instance of the JavaScript Function
class, and hence an object.
RECALL - In object-oriented programming, object is nothing fancy but an instance of a class, whereas class is a blueprint that you can use multiple times to instantiate an object.
We have taken a look at an example which shows that function in JavaScript is basically just an instance of the Function
class. To make it more convincing, here I will show how one can instantiate a function using the new
operator just like any other objects in JavaScript.
// Object instantiation in JavaScript might look something like this.
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
const welvin = new Person('Welvin Bun', 19);
console.log(welvin); // prints Person { name: 'Welvin Bun', age: 19 }
Like what we have seen before, function is just an instance of the built-in Function
class. With that being said, we can actually instantiate a function directly using the new
operator and Function
class constructor just like in the example above where we use the new
operator and Person
class constructor to instantiate a Person
object.
const functionBody = 'return a + b';
const addTwoNumbers = new Function('a', 'b', functionBody);
console.log(addTwoNumbers(1, 2)); // prints 3
NOTE - Please be noted that this way of creating a function might not align with the best practice out there. I rarely if not never see someone using this approach before. You should use the function
keyword or the infamous ES6 arrow function syntax instead.
As we've already seen in the previous examples, JavaScript functions are internally treated as objects. Like any other objects, that means we can store the pointer reference of a JavaScript Function
object to a variable like in the code example below.
const addTwoNumbers = function (a, b) {
return a + b;
};
console.log(addTwoNumbers(1, 2)); // prints 3
Additionally, we can also declare a second variable and make it pointing to the same reference as what addTwoNumbers
variable is pointing to.
const addTwoNumbersCopy = addTwoNumbers;
console.log(addTwoNumbersCopy(27, 12)); // prints 39
Since addTwoNumbers
and addTwoNumbersCopy
share the same reference, modifying any member variable or property of one will directly affect the other.
console.log(addTwoNumbers.memberVariable); // prints undefined
addTwoNumbersCopy.memberVariable = 1;
console.log(addTwoNumbers.memberVariable); // prints 1
Just like any other types of variable in JavaScript, those of object type can be dinamically passed as arguments to another function call. Knowing that JavaScript function is nothing but an object, that means we can actually feed it into another function as one of the arguments.
function printTheProductOfTwoNumbers(a, b, anotherFunction) {
console.log(a * b);
anotherFunction();
}
function printHello() {
console.log('Hello!');
}
printTheProductOfTwoNumbers(5, 5, printHello);
// prints 25
// prints Hello!
TRIVIA - The term First-Class Functions was first coined by a British Computer Scientist, Christopher Strachey in 1960s because it behaves like the first-class citizens of programming.
Now that we all know how JavaScript functions are represented internally and what First-Class Functions really is. Let's take a deeper look at the main point of this post.
Higher-Order Function is a term shared widely across the field of Mathematics and Computer Science, especially when we are talking about the Functional Programming paradigm. It is basically just a regular function with some more specific features that makes it special. A function can be classified as Higher-Order Functions if at least one of the following condition applies:
Let's take a look at some examples to get a better understanding.
Let's begin with a function . In your opinion, do you think the function is a Higher-Order Function?
The answer is no, it's not. Recall the two properties that a Higher-Order Function must have (at least one). Does the function take one or more functions as its input argument(s)? Well it's well-defined that takes one scalar integer argument named . Since as the only input of is not a function, hence does not comply with the first rule that we stated before.
The second question to ask is, does the function return another function as its output? Again, it can be seen clearly that what does is just adding a scalar value of 1 to its input , and returning that addition value as the output. Therefore, returns another scalar value and not a function, so neither the first nor the second condition is met.
With that being said, we can conclude that does not satisfy any of the requirements of a Higher-Order Function, and hence it's not of its kind.
Let's move on to our second function , where is differentiable for any . Is a Higher-Order Function?
Indeed it is. Using the same logic that we used to analyse our first function , it's not hard to show that is a Higher-Order Function. First, it accepts another function as its input argument. In addition to that, it also returns another function as its output where is the first derivative of .
NOTE - is a quiet common operator in mathematics called the Differential operator. The semantic of it is basically it maps any differentiable functions to their corresponding first-order derivatives.
Now that you are familiar with some distinguishable features of Higher-Order Function, let's see some of the built-in JavaScript Higher-Order Functions and how they are usually being used.
Let's say you have an array of numbers representing the heights (in cm) of a group of people whose heights you measured before. After getting 5 data entries, you suddenly realized that the scale which was used for the measurement processes is off by 5 cm. The goal is to add 5 centimeters to each data entry in the array. One obvious solution would be:
const heights = [170, 168, 185, 169, 168];
const ERROR = 5;
const heightsFixed = [];
for (const height of heights) {
heightsFixed.push(height + ERROR);
}
console.log(heightsFixed); // prints [ 175, 173, 190, 174, 173 ]
Using Array.prototype.map
method, you can achieve the same goal with a way more efficient and readable code. Array.prototype.map
takes a function of 3 parameters ((element, index, array)
) this input function is commonly called as callback function. It then iterates through each element in the array, feed the corresponding (element, index, array)
to the callback function, takes the return value of the callback function and create a new array based on these return values. Let's take a look at the code example to get the hang of what I just explained.
const heights = [170, 168, 185, 169, 168];
const ERROR = 5;
function fixHeight(element, index, array) {
return element + ERROR;
}
const heightsFixed = heights.map(fixHeight);
console.log(heightsFixed); // prints [ 175, 173, 190, 174, 173 ]
Breaking down the piece of code above, here fixHeight
is what we refer to as a callback function. It is just a function which is passed as an argument to another function.
You might have also noticed that there are 3 parameters in the fixHeight
function definition. These 3 parameters will change every time we arrive at a new array entry. For example, in the example above, fixHeight
will be executed 5 times, one execution each for each element in the heights
array. And the corresponding callback function arguments would be:
const heights = [170, 168, 185, 169, 168];
const ERROR = 5;
function fixHeight(element, index, array) {
console.log({ element, index, array });
}
heights.map(fixHeight);
//prints
//{ element: 170, index: 0, array: [ 170, 168, 185, 169, 168 ] }
//{ element: 168, index: 1, array: [ 170, 168, 185, 169, 168 ] }
//{ element: 185, index: 2, array: [ 170, 168, 185, 169, 168 ] }
//{ element: 169, index: 3, array: [ 170, 168, 185, 169, 168 ] }
//{ element: 168, index: 4, array: [ 170, 168, 185, 169, 168 ] }
The implementation could indeed be made a little bit simpler.
const heights = [170, 168, 185, 169, 168];
const ERROR = 5;
const heightsFixed = heights.map(function (element, index, array) {
return element + ERROR;
});
console.log(heightsFixed); // prints [ 175, 173, 190, 174, 173 ]
RECALL - The two implementations we discussed just now are made possible due to the object-like behaviour of functions in JavaScript. With that, we are able to pass the callback function around as an argument to another function.
Let's say you have an array of object representing some informations of a particular student. Each object in the array is an instance of the Student
class with the following class definition:
class Student {
constructor(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender; // Assume gender can only take a value of "M" or "F"
}
}
Your task is, given an array named students
consists of multiple Student
object, separate the entries into two different arrays femaleStudents
and maleStudents
, each consists only of female and male students respectively. The trivial solution would look something like this:
const student1 = new Student('Welvin Bun', 19, 'M');
const student2 = new Student('Edward Suryajaya', 19, 'M');
const student3 = new Student('Michael Hosanen', 19, 'M');
const student4 = new Student('Jesstlyn Clarissa', 19, 'F');
const students = [student1, student2, student3, student4];
const maleStudents = [];
const femaleStudents = [];
for (const student of students) {
if (student.gender === 'F') {
femaleStudents.push(student);
} else {
maleStudents.push(student);
}
}
console.log(maleStudents);
// prints [ Student { name: 'Welvin Bun', age: 19, gender: 'M' },
// Student { name: 'Edward Suryajaya', age: 19, gender: 'M' },
// Student { name: 'Michael Hosanen', age: 19, gender: 'M' } ]
console.log(femaleStudents); // prints [ Student { name: 'Jesstlyn Clarissa', age: 19, gender: 'F' } ]
The Array.prototype.filter
takes the same input format as its counterpart, Array.prototype.map
. The difference is Array.prototype.filter
will treat the output value of the given callback function as a boolean value. According to this boolean value, it will only include any element whose callback result is True
to the new array.
const maleStudents = students.filter(function (element, index, array) {
return element.gender === 'M';
});
// Using ES6 arrow function style
const femaleStudents = students.filter(
(element, index, array) => element.gender === 'F'
);
console.log(maleStudents);
// prints [ Student { name: 'Welvin Bun', age: 19, gender: 'M' },
// Student { name: 'Edward Suryajaya', age: 19, gender: 'M' },
// Student { name: 'Michael Hosanen', age: 19, gender: 'M' } ]
console.log(femaleStudents); // prints [ Student { name: 'Jesstlyn Clarissa', age: 19, gender: 'F' } ]
In conclusion, we have covered some fundamental concepts of First-Class Functions. You have seen some examples showing that JavaScript function is nothing but an instance of the built-in Function
class.
We also have taken 2 different perspectives when discussing about the idea behind Higher-Order Functions, from both Mathematics and Computer Science point of views. They basically refer to the same concept with slightly different implementations.
Finally, we are all now must be familiar with some built-in JavaScript Higher-Order Functions and their use cases through the code examples provided.
That's it, I hope you enjoy reading my post and thanks for coming by!