JavaScript Core/
Lesson

In JavaScript, functions are first-class citizens, they can be assigned to variables, passed as arguments, and returned from other functions. Higher-order functions take advantage of this superpower. They're functions that operate on other functions, either by taking them as arguments or returning them.

What makes a function "higher-order"?

A higher-order function is any function that:

  • Takes one or more functions as arguments, OR
  • Returns a function as its result

You've already used higher-order functions: setTimeout, addEventListener, and array methods like map are all higher-order functions.

02

The big three array methods

Modern JavaScript provides three essential higher-order functions for working with arrays. Master these and you'll handle the vast majority of array transformations you'll need.

MethodPurposeReturnsMutates original?
map()Transform every elementNew array (same length)No
filter()Keep elements that pass a testNew array (same or fewer)No
reduce()Combine into one valueAny typeNo

map(): Transform every element

map creates a new array by applying a function to every element. It doesn't modify the original array.

const numbers = [1, 2, 3, 4, 5];

const doubled = numbers.map(n => n * 2);
// [2, 4, 6, 8, 10]

const names = ["alice", "bob", "charlie"];
const uppercased = names.map(name => name.toUpperCase());
// ["ALICE", "BOB", "CHARLIE"]

The callbackWhat is callback?A function you pass into another function to be called later, often when an operation finishes or an event occurs. receives three arguments:

  • The current element
  • The current indexWhat is index?A data structure the database maintains alongside a table so it can find rows by specific columns quickly instead of scanning everything.
  • The original array

const indexed = names.map((name, index) => `${index + 1}. ${name}`);
// ["1. alice", "2. bob", "3. charlie"]

filter(): Keep what matches

filter creates a new array with only elements that pass a test:

const numbers = [1, 2, 3, 4, 5, 6];

const evens = numbers.filter(n => n % 2 === 0);
// [2, 4, 6]

const greaterThanThree = numbers.filter(n => n > 3);
// [4, 5, 6]

The callback should return true to keep the element, false to filter it out:

const users = [
  { name: "Alice", age: 25, active: true },
  { name: "Bob", age: 17, active: true },
  { name: "Charlie", age: 30, active: false }
];

const activeAdults = users.filter(user =>
  user.active && user.age >= 18
);
// [{ name: "Alice", age: 25, active: true }]

reduce(): Combine into one value

reduce takes an array and reduces it to a single value. It's the most powerful and most complex of the three.

const numbers = [1, 2, 3, 4, 5];

const sum = numbers.reduce((accumulator, current) => {
  return accumulator + current;
}, 0);
// 15

The reduce callback receives:

  • accumulator: the running total (or whatever you're building)
  • current: the current element being processed
  • index: the current index (optional)
  • array: the original array (optional)

The second argument to reduce (0 in the example) is the initial value for the accumulator.

AI pitfall
AI tools generate overly complex callback chains when simpler code would do. They'll nest .map().filter().reduce() three levels deep, or use reduce to build something that a simple for...of loop expresses more clearly. If you can't read an AI-generated chain in 10 seconds, refactor it. Readable code beats clever code every time.

Practical reduce examples

Build an object from an array:

const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 3, name: "Charlie" }
];

const userMap = users.reduce((map, user) => {
  map[user.id] = user;
  return map;
}, {});

// { 1: {id: 1, name: "Alice"}, 2: {...}, 3: {...} }
console.log(userMap[2].name); // "Bob"

Group items by category:

const products = [
  { name: "Apple", category: "fruit" },
  { name: "Carrot", category: "vegetable" },
  { name: "Banana", category: "fruit" }
];

const grouped = products.reduce((groups, product) => {
  const category = product.category;
  if (!groups[category]) {
    groups[category] = [];
  }
  groups[category].push(product);
  return groups;
}, {});

// { fruit: [{...}, {...}], vegetable: [{...}] }
03

Chaining array methods

These methods return arrays (except reduce), so you can chain them together:

const users = [
  { name: "Alice", age: 25, active: true },
  { name: "Bob", age: 17, active: true },
  { name: "Charlie", age: 30, active: false },
  { name: "Diana", age: 22, active: true }
];

// Get names of active adults, sorted by age
const activeAdultNames = users
  .filter(u => u.active && u.age >= 18)  // Keep active adults
  .map(u => u.name);                      // Extract names

console.log(activeAdultNames);  // ["Alice", "Diana"]
Performance consideration
Each method in the chain creates a new array. For very large datasets (millions of items), consider using a single reduce() or a for...of loop instead of long chains. For typical use cases (hundreds or thousands of items), chaining is perfectly fine and much more readable.
04

Writing your own higher-order functions

You can create your own higher-order functions:

// Takes a callback and calls it
function withLogging(callback) {
  console.log("Starting operation...");
  const result = callback();
  console.log("Operation complete!");
  return result;
}

withLogging(() => {
  console.log("Doing the work");
  return 42;
});
// Logs:
// Starting operation...
// Doing the work
// Operation complete!

Returning a function (function factory):

function multiplyBy(factor) {
  return function(number) {
    return number * factor;
  };
}

const triple = multiplyBy(3);
const double = multiplyBy(2);

console.log(triple(5)); // 15
console.log(double(5)); // 10
05

Quick reference

MethodWhat it doesReturns
map()Transform each elementNew array
filter()Keep matching elementsNew array
reduce()Combine into one valueAny type
forEach()Run function for each elementundefined
find()First matching elementElement or undefined
findIndex()Index of first matchIndex or -1
some()Test if any element passestrue or false
every()Test if all elements passtrue or false
javascript
const numbers = [1, 2, 3, 4, 5];

// map: transform each element
const doubled = numbers.map(n => n * 2);
// [2, 4, 6, 8, 10]

// filter: keep elements that pass a test
const evens = numbers.filter(n => n % 2 === 0);
// [2, 4]

// reduce: combine into single value
const sum = numbers.reduce((acc, n) => acc + n, 0);
// 15

// Chaining methods
const result = numbers
  .filter(n => n > 2)
  .map(n => n * 10);
// [30, 40, 50]

// find and some
console.log(numbers.find(n => n > 3)); // 4
console.log(numbers.some(n => n > 4)); // true