Fundamentals 11 min read

Key Concepts of Functional Programming in JavaScript

This article explains core functional programming concepts in JavaScript—including pure functions, side effects, currying, composition, and pointfree style—detailing their definitions, advantages, practical code examples, and how they improve code clarity, testability, and reusability.

ByteFE
ByteFE
ByteFE
Key Concepts of Functional Programming in JavaScript

Some Necessary Concepts

Pure Function : A function that, given the same input, always returns the same output and has no observable side effects.

Pure functions are the mathematical functions that form the basis of functional programming.

Side Effects : Any interaction with the external environment or change of system state during computation, such as modifying the file system, writing to a database, sending HTTP requests, changing state, logging, DOM queries, or accessing system state.

In short, any function that interacts with the outside world has side effects.

Currying

Currying transforms a function that takes multiple arguments into a sequence of functions each taking a single argument.

const add = x => y => x + y;
const increment = add(1);
const addTen = add(10);
increment(2); // 3
addTen(2); // 12

Advantages of Functional Programming

Determinism: identical inputs always produce identical outputs.

Mathematical laws (associativity, commutativity, identity, distributivity) can be applied.

// associative
add(add(x, y), z) === add(x, add(y, z));
// commutative
add(x, y) === add(y, x);
// identity
add(x, 0) === x;
// distributive
multiply(x, add(y, z)) === add(multiply(x, y), multiply(x, z));

Applicable Scenarios for Functional Programming

Mutable state

Unrestricted side effects

Unprincipled design

Functions as First-Class Citizens

In JavaScript, functions can be stored in arrays, passed as arguments, assigned to variables, etc., which enables reduction of unnecessary refactoring, increased reusability, and avoidance of this when properly adapted.

// example without this
httpGet('/post/2', renderPost);

Value of Pure Functions

Cacheable

const memoize = f => {
  const cache = {};
  return (...args) => {
    const key = JSON.stringify(args);
    cache[key] = cache[key] || f(...args);
    return cache[key];
  };
};
const squareNumber = memoize(x => x * x);
squareNumber(4); // 16
squareNumber(4); // 16 (cached)

Portable / Self‑documenting

// impure version
const signUp = attrs => {
  const user = saveUser(attrs);
  welcomeUser(user);
};
// pure version
const signUp = (Db, Email, attrs) => () => {
  const user = saveUser(Db, attrs);
  welcomeUser(Email, user);
};

Pure functions expose all dependencies via their signatures, improving portability and testability.

Testable: provide inputs and assert outputs.

Reasonable: easier to reason about.

Parallelizable: no shared mutable state.

Curry Implementation

// curry :: ((a, b, ...) -> c) -> a -> b -> ... -> c
function curry(fn) {
  const arity = fn.length;
  return function $curry(...args) {
    if (args.length < arity) {
      return $curry.bind(null, ...args);
    }
    return fn.call(null, ...args);
  };
}
const match = curry((what, s) => s.match(what));
const replace = curry((what, replacement, s) => s.replace(what, replacement));
const filter = curry((f, xs) => xs.filter(f));
const map = curry((f, xs) => xs.map(f));

Currying lets us create reusable, concise functions such as hasLetterR = match(/r/g) and compose them with other utilities.

Composition

const compose = (...fns) => (...args) => fns.reduceRight((res, fn) => [fn.call(null, ...res)], args)[0];
const toUpperCase = x => x.toUpperCase();
const exclaim = x => `${x}!`;
const shout = compose(exclaim, toUpperCase);
shout('send in the clowns'); // "SEND IN THE CLOWNS!"

Because compose is itself a pure function, it satisfies distributive law and yields consistent results regardless of argument order.

Pointfree Style

Pointfree programming composes operations without explicitly mentioning the data being transformed.

var str = 'Lorem ipsum dolor sit amet';
var splitBySpace = s => s.split(' ');
var getLength = w => w.length;
var getLengthArr = arr => R.map(getLength, arr);
var getBiggerNumber = (a, b) => a > b ? a : b;
var findBiggestNumber = arr => R.reduce(getBiggerNumber, 0, arr);
var getLongestWordLength = R.pipe(splitBySpace, getLengthArr, findBiggestNumber);
getLongestWordLength(str); // 11

Using libraries like Ramda, lodash, or Folktale provides ready‑made pointfree utilities.

Reference

Professor Frisby’s Mostly Adequate Guide to Functional Programming (JS translation)

Pointfree JavaScript

Favoring Curry

JavaScriptFunctional ProgrammingCurryingpure functionscompositionpointfree
ByteFE
Written by

ByteFE

Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.