Functional Programming in JavaScript

Published February 15, 2021

JavaScript allows developers to mix between object-oriented, procedural, and functional programming paradigms. The growing trend lately has been toward functional programming. In JavaScript frameworks such as Angular and React, using functional programming can even result in performance improvements.

Functional programming is about programming with very small and reusable pure functions.

Pure functions

Just because a program contains functions, it does not mean that the program uses functional programming. Functional programming is based on the concepts of pure and impure functions. Functional programming encourages you to write pure functions.

Pure functions are functions that accept an input and return a value, without producing any side-effects. No side-effects means that the function does not modify any data outside of its scope. A pure function must return a value. The return value of a pure function must depend on the arguments that are passed to the parameters of the function.

Properties of pure functions

A pure function must satisfy the following properties to be considered pure:

  • Idempotence: Given the same inputs, a pure function will always return the same value, regardless of how many times the function is called. This means that a pure function does not depend on any external state that can be mutated during execution.

  • Side-effect free: A pure function cannot cause any side-effects. Side effects may include I/O (writing to the console or to a log file), modifying a mutable object, reassigning a variable, etc. Pure functions do not mutate any shared state or function arguments. Pure function calls don't result in any observable output, don't throw exceptions, and don't trigger events. They just return a value.

Pure functions are not very likely to cause bugs in other unrelated parts of a program because they do not operate on a shared state, not do they produce side-effects.

If a function is using void (it has no return value), it is an impure function. If a function has no return value, then either it's not doing any operations or it's causing some side-effect, and thus, is an impure function. If a function is called but its return value is not used, it may be relied on to do some side-effect, making it an impure function.

Examples

function impure(arg) {
  myObject.key = 90;
  return arg * myObject.key;
}

The above function is not a pure function because it modified the state of myObject.key outside of its scope.

function impure(arg) {
  let f = arg * myObject.key;
}

Though it didn’t modify any external state, the above function is also not a pure function because it did not return a value.

function impure(arg) {
  return myObject.key * 3;
}

Though it didn’t affect any external state of myObject.key, the above function is impure because the returned value is not dependent on the input arg. Not only must pure function return a value but that returned value must depend on the input.

function pure(arg) {
  return arg * 4;
}

The above function is a pure function. It returned an output based on the input and did not create side-effects on any any external state.

Benefits of pure functions

  • They are easier to work with and debug because they don't depend on a mutable external state.
  • The return value can be cached or memoized to avoid recomputing it in the future.
  • They make it easier to write unit tests because there's no dependencies that need to be mocked (such as for logging, database transactions, API calls, etc).
  • Replacing procedural loops with pure functions increases code readability.

Encapsulation

Pure functions provide the same benefits that encapsulation does in Object-Oriented Programming. Encapsulation is about data-hiding. It's about minimizing connections with outside code to reduce the likelihood of unexpected changes. What can be hidden and self-contained, can be easily changed.

Pure functions follow the encapsulation principles of:

  • Being independent from outside code.
  • Allowing the function implementation to change without impacting the rest of the program.
  • Allowing the code to be easily moved between files, modules, projects, etc.
  • Having a self-documenting public interface (the function signature).

Conclusion

Complex programs usually need to produce an output. Therefore, they cannot be built with only pure functions. However, it is a good programming practice to make functions pure whenever possible. Adhering to functional programming principles can help to achieve this.

Functional programming may require a learning curve if you are coming from an Object-Oriented Programming background. You would need to look out for cases where objects get mutated, where state is shared, where logic extends beyond the function scope, and where side-effects are caused by function calls.