Functional Programming in JavaScript – Functions as First-Class Citizens
Yesterday, I explored JavaScript Prototypes and Prototypal Inheritance and understood how JavaScript's object system works underneath the class syntax.
Today, I shifted my focus from objects and inheritance to another important programming style:
Functional Programming.
JavaScript treats functions as first-class citizens, which means functions can be stored in variables, passed as arguments, and returned from other functions.
I explored:
First-Class Functions
Callback Functions
Higher-Order Functions
Pure Functions
Side Effects
map()filter()reduce()forEach()
Let's dive in.
What is Functional Programming?
Functional Programming (FP) is a programming style where functions are treated as the primary building blocks of a program.
Instead of focusing mainly on changing data and object state, functional programming often emphasizes:
Reusable functions
Predictable behavior
Avoiding unnecessary side effects
Transforming data
Composing smaller functions
JavaScript supports functional programming because functions are first-class values.
Functions Are First-Class Citizens
In JavaScript, functions can be treated like any other value.
We can store a function inside a variable:
const greet = function () {
console.log("Hello!");
};
Then call it:
greet();
We can also store an arrow function:
const add = (a, b) => a + b;
console.log(add(10, 20));
Functions can therefore be:
Stored in variables
Passed to other functions
Returned from functions
Stored inside arrays and objects
Passing Functions as Arguments
We can pass a function to another function.
function greetUser(name) {
console.log(`Hello ${name}`);
}
function processUser(callback) {
callback("Saurabh");
}
processUser(greetUser);
Here:
greetUser
↓
passed to
↓
processUser
↓
callback()
This is the foundation of callback functions.
What is a Callback Function?
A callback is a function passed into another function so that it can be executed later or at a specific point.
Example:
function calculate(a, b, callback) {
return callback(a, b);
}
function add(a, b) {
return a + b;
}
console.log(
calculate(10, 20, add)
);
Output:
30
The add function is being passed as a callback.
Higher-Order Functions
A higher-order function is a function that:
Accepts another function as an argument
Returns a function
Or does both
Example:
function operate(a, b, operation) {
return operation(a, b);
}
const result = operate(
10,
5,
(a, b) => a * b
);
console.log(result);
Here operate() is a higher-order function because it accepts another function.
Why Higher-Order Functions Matter
Higher-order functions allow us to create reusable logic.
Instead of writing:
function addNumbers(a, b) {
return a + b;
}
function multiplyNumbers(a, b) {
return a * b;
}
we can create a generic operation:
function calculate(a, b, operation) {
return operation(a, b);
}
Now the behavior can be changed by passing different functions.
Pure Functions
A pure function is a function that:
Produces the same output for the same input.
Doesn't modify external state or cause observable side effects.
Example:
function add(a, b) {
return a + b;
}
Every time we call:
add(10, 20);
we get:
30
The function doesn't depend on outside variables.
Impure Functions
Consider:
let total = 0;
function addToTotal(amount) {
total += amount;
}
The function modifies external state.
Its behavior depends on the current value of total.
This makes it an impure function.
Impure functions aren't always bad, but understanding side effects helps us control application complexity.
What are Side Effects?
A side effect is something a function does beyond simply returning a value.
Examples include:
Changing a global variable
Modifying an object outside the function
Updating the DOM
Writing to storage
Making an API request
Logging to the console
For example:
document.body.textContent = "Hello";
This changes something outside the function's return value.
Array Methods
Functional programming becomes especially useful when working with arrays.
JavaScript provides methods such as:
map()
filter()
reduce()
forEach()
These methods commonly accept callback functions.
forEach()
forEach() executes a function once for every element.
const numbers = [1, 2, 3, 4];
numbers.forEach((number) => {
console.log(number);
});
Output:
1
2
3
4
forEach() is useful when we want to perform an action for each item.
map()
map() creates a new array by transforming each element.
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(
number => number * 2
);
console.log(doubled);
Output:
[2, 4, 6, 8]
The original array remains unchanged.
filter()
filter() creates a new array containing only elements that satisfy a condition.
const numbers = [
1, 2, 3, 4, 5, 6
];
const evenNumbers = numbers.filter(
number => number % 2 === 0
);
console.log(evenNumbers);
Output:
[2, 4, 6]
reduce()
reduce() is used to combine array elements into a single accumulated value.
For example, calculating a total:
const numbers = [10, 20, 30];
const total = numbers.reduce(
(sum, number) => sum + number,
0
);
console.log(total);
Output:
60
The second argument:
0
is the initial accumulator value.
map() vs filter() vs reduce()
| Method | Purpose | Returns |
|---|---|---|
forEach() |
Perform an action | undefined |
map() |
Transform elements | New array |
filter() |
Select elements | New array |
reduce() |
Combine elements | Single value |
A simple way to remember them:
map → Transform
filter → Select
reduce → Combine
forEach → Perform an action
Chaining Array Methods
We can combine these methods.
Example:
const numbers = [
1, 2, 3, 4, 5, 6
];
const result = numbers
.filter(number => number % 2 === 0)
.map(number => number * 2);
console.log(result);
Output:
[4, 8, 12]
The process is:
Original Array
↓
filter()
↓
Even Numbers
↓
map()
↓
Doubled Numbers
This style is extremely common in modern JavaScript.
Functional Programming and React
These concepts become especially important when working with React.
For example:
const names = users.map(
user => user.name
);
Or filtering data:
const activeUsers = users.filter(
user => user.active
);
These patterns appear constantly when building user interfaces.
Avoiding Mutation
Functional programming often encourages avoiding unnecessary mutation.
Instead of:
const numbers = [1, 2, 3];
numbers.push(4);
we can create a new array:
const numbers = [1, 2, 3];
const updatedNumbers = [
...numbers,
4
];
Now the original array remains unchanged.
This idea becomes particularly important when working with React state.
Best Practices
✔ Keep functions small and focused.
✔ Prefer pure functions when practical.
✔ Avoid unnecessary mutation.
✔ Use map() for transformation.
✔ Use filter() for selection.
✔ Use reduce() for accumulation.
✔ Use forEach() when you simply need to perform an action for each element.
✔ Use meaningful callback parameter names.
My Biggest Takeaway
Today, I learned that JavaScript functions are much more powerful than simply blocks of reusable code.
Because functions are first-class values, I can pass them around, return them, and use them to build flexible abstractions.
The array methods were especially important:
map() → Transform
filter() → Select
reduce() → Combine
forEach() → Perform an action
These concepts are going to be extremely useful as I move toward React, because modern frontend development relies heavily on transforming and rendering collections of data.
