Functional Programming in JavaScript

Functional programming is a programming paradigm that emphasizes the use of functions to transform data. It aims to write code that is more declarative, making it easier to reason about and maintain over time. JavaScript has been embraced by the functional programming community as a language that can be used to implement functional programming concepts.

In this article, we will explore the fundamental concepts of functional programming in JavaScript and how to apply them to write clean, concise, and maintainable code.

First, let’s define some terms that are commonly used in functional programming.

  • Functional Composition: It refers to the process of composing functions to create more complex functions. The output of one function becomes the input to another function, allowing us to build complex logic with simple, reusable functions.
  • Pure Function: It’s a function that has no side effects and always returns the same output given the same inputs. The output of a pure function only depends on its inputs and not on any external state.
  • Immutable Data: It’s data that cannot be modified after it has been created. This makes it easier to reason about the state of the application, as well as making it easier to reason about concurrency.
  • Higher-Order Functions: Functions that take other functions as inputs or return functions as outputs are known as higher-order functions. Higher-order functions are a key concept in functional programming, as they allow us to abstract away common patterns and build reusable components.
  • Currying: Currying is a technique for transforming a function with multiple arguments into a series of functions with a single argument. This allows us to write functions that can be partially applied, making them more flexible and easier to use.

Now that we have a basic understanding of these terms, let’s look at some of the key concepts of functional programming in JavaScript.

Function Composition

Function composition is the process of composing functions to create more complex functions. The output of one function becomes the input to another function, allowing us to build complex logic with simple, reusable functions.

In JavaScript, function composition can be achieved by using the compose function:

const compose =
  (...fns) =>
  (x) =>
    fns.reduceRight((acc, fn) => fn(acc), x);

const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
const addThenMultiply = compose(multiply, add);
console.log(addThenMultiply(3, 4, 5)); // Outputs 35

In this code, we use the compose function to create a new function that first adds its two arguments and then multiplies the result by the third argument. This allows us to build complex logic with simple, reusable functions, making our code easier to understand and maintain.

Pure function

Pure functions are a core concept in functional programming and are widely used in JavaScript. A pure function is a function that, given the same inputs, will always return the same output, and has no side effects. This makes pure functions predictable, testable, and easy to reason about, which are all important properties for writing maintainable and scalable code.

A function is considered pure if it meets the following criteria:

  1. The function always returns the same output for the same inputs.
  2. The function has no side effects, meaning it does not modify external state, such as global variables or the properties of objects outside of its scope.
  3. The function does not depend on external state, meaning that it only uses its inputs and local variables to calculate its output.

Consider the following example of a pure function in JavaScript:

const add = (a, b) => a + b;

This function takes two arguments, a and b, and returns their sum. Given the same inputs, it will always return the same output, and it does not modify any external state or depend on any external state.

Pure functions have several benefits:

  1. They are easy to reason about. Since pure functions always return the same output for the same inputs, it is easy to understand what a function does and what its output will be.
  2. They are testable. Since pure functions are deterministic, we can write tests that verify their behavior, making it easier to catch bugs and improve the quality of our code.
  3. They are composable. Pure functions can be combined and reused to build more complex functions, making it easier to write modular and maintainable code.
  4. They are cacheable. The output of a pure function can be cached, which can improve the performance of an application, especially when the function is computationally expensive.

Immutable Data

In functional programming, data is treated as immutable. This means that once data is created, it cannot be changed. This can be achieved in JavaScript by using const to declare variables that cannot be reassigned and by using Object.freeze to make objects immutable.

const num = 42;
num = 0; // TypeError: Assignment to constant variable.

const obj = { name: "John" };
Object.freeze(obj);
obj.name = "Jane"; // The object remains unchanged.

Using immutable data has many benefits, including making it easier to reason about the state of the application and making it easier to reason about concurrency. When you know that data cannot change, you can avoid having to track changes, making your code simpler and easier to understand.

Higher-Order Functions

Higher-order functions are functions that take other functions as inputs or return functions as outputs. Higher-order functions are a key concept in functional programming, as they allow us to abstract away common patterns and build reusable components.

For example, consider the following code:

const repeat = (fn, n) => {
  for (let i = 0; i < n; i++) {
    fn();
  }
};

const hello = () => console.log("Hello!");
repeat(hello, 3); // Prints "Hello!" three times.

In this code, repeat is a higher-order function because it takes a function as an input. The function repeat can be used to repeat any function n times, making it a reusable component that can be used throughout the application. This is just one example of how higher-order functions can be used to abstract away common patterns and make our code more flexible and maintainable.

Currying

Currying is a technique for transforming a function with multiple arguments into a series of functions with a single argument. This allows us to write functions that can be partially applied, making them more flexible and easier to use.

In JavaScript, currying can be achieved by using closures. A closure is a function that remembers the values of its outer scope even after the function has returned.

Consider the following code:

const curry = (fn) => {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(null, args);
    } else {
      return function (...args2) {
        return curried.apply(null, args.concat(args2));
      };
    }
  };
};

const add = (a, b) => a + b;
const curriedAdd = curry(add);
const add5 = curriedAdd(5);
console.log(add5(3)); // Outputs 8

In this code, we use a closure to create a curried version of the add function. The curriedAdd function takes a single argument and returns a new function that takes the second argument. This allows us to partially apply the function and create new functions with specific values for some of its arguments.

Conclusion

Functional programming is a powerful and elegant programming paradigm that has many benefits, including making it easier to reason about the state of the application, making it easier to reason about concurrency, and making it easier to write maintainable code. JavaScript is a versatile and popular programming language that has been embraced by the functional programming community as a language that can be used to implement functional programming concepts.

In this article, we have explored the fundamental concepts of functional programming in JavaScript, including immutable data, higher-order functions, currying, and function composition. By understanding these concepts, you will be able to write cleaner, more concise, and more maintainable code in JavaScript.

Like this type of content? Drop a reaction below! Leave a comment if you have any questions or comments.

FAQ

What is functional programming?

Functional programming is a programming paradigm that emphasizes the use of functions and the avoidance of changing-state and mutable data.

Is JavaScript a functional programming language?

JavaScript is a multi-paradigm programming language, meaning that it supports both functional and object-oriented programming styles.

Are there other functional programming languages?

Yes. Haskell, Lisp, Scheme, and many other languages are functional programming languages.

comments powered by Disqus

Related Posts

Creating a NodeJS Budgeting Tool

In this article, we’re going to show you how to read and write to a .txt file using NodeJS by creating a small budgeting CLI (Command Line Interface).

Read more

Becoming a Hacker: Must Read Security & Cyber Crime Books

In our most recent publication, we delved into security and cyber crime blogs you should be reading to stay up-to-date with modern threats, bugs, and crimes.

Read more

Defang an IP Address with JavaScript

I was asked this question long ago. At first, I was stumped. Not because the question is tricky, but because my brain shuts down when multiple people are stairing at the back of my head while I’m standing in front of a white board.

Read more