Often many people confused about nested function in JavaScript, why the nested function is useful, and what is the use case for nested function? There are so many places we might encounter such nested functions in our code or blog post or articles.
In this article, I would like to talk about the basics of why nested functions are useful in many ways.
I'll start asking some basic questions along with some constraints that way it may help to think differently to solve the problem. Let's assume that we have employee array like below
const employee = [
{ id: 1, name: "Raja", age: 28, status: "active" },
{ id: 2, name: "Andy", age: 32, status: "Inactive" },
{ id: 3, name: "Kumar", age: 45, status: "active" },
{ id: 4, name: "Charles", age: 35, status: "Inactive" },
];
Use case 1 #
How do you sort an array by property (age, status, etc)?
Constraint: Do not pass an array as a reference to any other functions and function should be more reusable.
Typically, with our prior programming knowledge, we do sorting using Array.prototype.sort function like below
function sortBy(array, propName) {
return array.sort(function (a, b) {
if (a[propName] > b[propName]) {
return 1;
} else if (a[propName] < b[propName]) {
return -1;
} else {
return 0;
}
});
}
console.log(sortBy(employee, "age"));
console.log(sortBy(employee, "status"));
The above code does the job nicely but just take a look into our first constraint for the first use case. so it's clearly saying that's not the way to do. Note that, we are passing array reference to the function :(
In general, this is how we do sorting in JavaScript
// How do to pass another arguments to tell sort based on which property of an object ???????
Array.prototype.sort(function(a, b))
As we know Array.prototype.sort interface only accepts 2 arguments, So How do we pass propName as another argument? now we clearly understand that sort method accepts only 2 parameters we can't pass any extra argument to the function anymore.
Okay then how do we solve the problem?.
Hint: can we make our custom argument accessible inside sort callback function without adding an extra argument to Array.prototype.sort?
This is a perfect moment to use function inside a function (often called higher-order function) so that we could return a function which accepts only 2 arguments as per sort interface requirement, the high order function can accept n of arguments so that we can access our custom argument inside the sort callback function.
Here is the second version of sortBy function
function sortBy(propName) {
return function (a, b) { // obeying sort interface function signature
if (a[propName] > b[propName]) { // propName accessible from highorder function
return 1;
} else if (a[propName] < b[propName]) {
return -1;
} else {
return 0;
}
};
}
console.log(employee.sort(sortBy("age")));
console.log(employee.sort(sortBy("status")));
I would like to discuss one more use case for higher-order functions with filter use case
Use case 2:
How do you filter an array with an inactive employee?
Constraint: Assume that we have function for filter out all active employee(isActiveEmployee) function already exists, Use the same function to get all inactive employee without code duplication.
function isActiveEmployee(employee) {
return employee.status == 'active';
}
const activeEmployee = employee.filter(isActiveEmployee);
// const inactiveEmployee = employee.filter(??????);
Okay we might come up with below solution easy without thinking anything
function isInactiveEmployee(employee) {
return employee.status !== 'active';
}
//(or)
function isInactiveEmployee(employee) {
return employee.status === 'inactive';
}
Just gentle reminder of use case 2's constraint, it said, we should n't duplicate the code, so can we do it a better way?
So think of we have already code which finds all active employees, so it just to invert the result of isActiveEmployee function we will get the all inactive employee even without write a brand new function for filter out inactive employee.
Method looks like
function negate(item){
return !isActiveEmployee(item)
}
But we can do even better, we may think of negate as utility functions so it can be written like below
function negate(predicate){
return function() {
return !predicate.apply(this, arguments)
}
}
Here is solution for filtered out all inactive employee
const inactiveEmployee = employee.filter(negate(isActiveEmployee));
console.log({inactiveEmployee});
Hope now you can see some of the benefits of high order function such as
- Cleaner abstraction
- working with composable functions and many more
Further Readings:
https://eloquentjavascript.net/05_higher_order.html
https://javascript.info/currying-partials