Чистые функции в JavaScript

Чистая функция - это функция, которая при одинаковых аргументах всегда возвращает одинаковый результат и не меняет ничего снаружи.

function sum(a, b) {
  return a + b;
}

console.log(sum(2, 3));
console.log(sum(2, 3));

Результат:

5
5

Функция sum() зависит только от аргументов a и b. Она ничего не меняет во внешнем коде.

Что такое чистая функция

У чистой функции есть два главных признака:

1. Одинаковые аргументы всегда дают одинаковый результат.

2. Функция не изменяет внешние данные.

Пример чистой функции:

function getDiscountPrice(price, discount) {
  return price - price * discount;
}

console.log(getDiscountPrice(1000, 0.1));

Результат:

900

Если снова вызвать getDiscountPrice(1000, 0.1), результат опять будет 900.

Одинаковый результат

Чистая функция не должна зависеть от случайных чисел, текущего времени или переменных, которые могут измениться снаружи.

function addTax(price) {
  return price * 1.2;
}

console.log(addTax(100));
console.log(addTax(100));

Результат:

120
120

При одном и том же значении price функция возвращает один и тот же результат.

А такая функция уже не будет чистой:

function getRandomBonus(price) {
  return price + Math.random() * 100;
}

Даже если передать одно и то же значение, результат может быть разным.

Без внешних изменений

Чистая функция не должна менять переменные, которые объявлены снаружи.

let total = 0;

function addToTotal(price) {
  total = total + price;

  return total;
}

console.log(addToTotal(100));
console.log(addToTotal(100));

Результат:

100
200

Функция меняет внешнюю переменную total. Поэтому она нечистая.

Чистый вариант можно записать так:

function getNewTotal(total, price) {
  return total + price;
}

console.log(getNewTotal(0, 100));
console.log(getNewTotal(0, 100));

Результат:

100
100

Функция получает все нужные данные через параметры и возвращает новое значение.

Нечистая функция

Нечистая функция - это не обязательно плохая функция. Иногда программе нужно менять данные, отправлять запросы или работать с DOM.

Например, функция, которая меняет текст на странице, не будет чистой.

function updateTitle(text) {
  document.title = text;
}

Она влияет на внешний мир - меняет заголовок страницы.

Важно не избегать нечистых функций любой ценой, а понимать, где они находятся и что именно меняют.

Работа с массивами

При работе с массивами чистая функция не должна менять исходный массив.

function addItem(items, item) {
  items.push(item);

  return items;
}

let fruits = ['яблоко', 'банан'];

console.log(addItem(fruits, 'груша'));
console.log(fruits);

Функция addItem() меняет массив fruits, который был передан в нее. Это нечистая функция.

Чистый вариант:

function addItem(items, item) {
  return [...items, item];
}

let fruits = ['яблоко', 'банан'];
let newFruits = addItem(fruits, 'груша');

console.log(newFruits);
console.log(fruits);

Результат:

['яблоко', 'банан', 'груша']
['яблоко', 'банан']

Исходный массив остался без изменений.

console.log() внутри функции

console.log() внутри функции тоже считается внешним действием.

function sum(a, b) {
  console.log(a + b);
}

Такая функция не возвращает результат, а сразу выводит его в консоль.

Чаще полезнее вернуть значение, а вывод сделать отдельно.

function sum(a, b) {
  return a + b;
}

console.log(sum(2, 3));

Так функцию проще переиспользовать: результат можно вывести, записать в переменную или передать дальше.

Зачем нужны чистые функции

Чистые функции легче читать, проверять и переиспользовать.

Если функция зависит только от аргументов, проще понять, почему она вернула именно такой результат.

Такие функции удобно тестировать: передали значения, получили ожидаемый результат.

function isAdult(age) {
  return age >= 18;
}

console.log(isAdult(20));
console.log(isAdult(15));

Результат:

true
false

Функция isAdult() ничего не меняет снаружи и всегда возвращает понятный результат для переданного возраста.

Итого

1. Чистая функция при одинаковых аргументах всегда возвращает одинаковый результат.

2. Чистая функция не меняет внешние переменные, массивы, объекты, DOM и другие внешние данные.

3. Если функция меняет что-то снаружи, она становится нечистой.

4. Нечистые функции нужны в реальных программах, но их лучше держать под контролем.

5. Чистые функции проще читать, тестировать и переиспользовать.