> For the complete documentation index, see [llms.txt](https://andersenlab.gitbook.io/javascript/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://andersenlab.gitbook.io/javascript/advanced-javascript/fp-in-javascript/functional-concepts.md).

# Functional concepts

## Чистота <a href="#id-019e" id="id-019e"></a>

### Чистые функции <a href="#id-2237" id="id-2237"></a>

Возвращаемое значение чистой функции зависит только от ее входных данных (аргументов) и не влечет никаких побочных эффектов. С одним и тем же входящим аргументом результат всегда будет одинаковый. Пример:

```javascript
function half(x) {
    return x / 2;
}
```

Функция`half(x)`принимает число`x`и возвращает значение половины`x`. Если мы передадим этой функции аргумент `8`, она вернет `4`. После вызова чистая функция всегда может быть заменена результатом своей работы. Например, мы могли бы заменить`half(8)`на `4`: где бы эта функция не использовалась в нашем коде, подмена никак не повлияла бы на конечный результат. Это называется [ссылочной прозрачностью](https://ru.wikipedia.org/wiki/Чистота_языка_программирования).

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

```javascript
var someNum = 8;

// это НЕ чистая функция
function impureHalf() {
  return someNum / 2;
}
```

Итого:

* Чистые функции должны принимать аргументы.
* Одни и те же входные данные (аргументы) всегда произведут одинаковые выходные данные (вернут одинаковый результат).
* Чистые функции основываются только на внутреннем состоянии и не изменяют внешнее (*примечание:*`console.log`*изменяет глобальное состояние*).
* Чистые функции не производят [побочных эффектов](https://ru.wikipedia.org/wiki/Побочный_эффект_\(программирование\)).
* Чистые функции не могут вызывать нечистые функции.

### Нечистые функции <a href="#id-8814" id="id-8814"></a>

Нечистая функция изменяет состояние вне своей области видимости. Любые функции с *побочными эффектами* — нечистые, ровно как и процедурные функции без возвращаемого значения. Рассмотрим следующие примеры:

```javascript
// нечистая функция производит побочный эффект
function showAlert() {
  alert('This is a side effect!');
}
// нечистая функция изменяет внешнее состояние
var globalVal = 1;
function incrementGlobalVal(x) {
  globalVal += x;
}
// нечистая функция процедурно вызывает чистую фунцию
function proceduralFn() {
  const result1 = pureFnFirst(1);
  const result2 = pureFnLast(2);
  console.log(`Done with ${result1} and ${result2}!`);
}
// нечистая функция выглядит чистой,
// но возвращает разные значения
// при одинаковых входных данных
function getRandomRange(min, max) {
  return Math.random() * (max - min) + min;
}
```

### Побочные эффекты в JavaScript <a href="#id-5a71" id="id-5a71"></a>

Когда функция или выражение изменяет состояние вне своего контекста, результат является побочным эффектом. Примеры побочных эффектов: вызов API, манипулирование DOM, вывод alert, запись в базу данных и так далее. Если функция производит побочные эффекты, она считается *нечистой*. Функции, вызывающие побочные эффекты, менее предсказуемы и их труднее тестировать, поскольку они приводят к изменениям вне их локальной области видимости.

### Вывод

Много качественного кода состоит из нечистых функций, процедурно вызывающихся чистыми. Это все равно несет массу преимуществ для тестирования и неизменяемости. Ссылочная прозрачность также обладает удобством для [мемоизации](https://ru.wikipedia.org/wiki/Мемоизация): кэширование и сохранение результатов вызова функций, а затем [переиспользование кэшированных результатов](https://www.sitepoint.com/implementing-memoization-in-javascript/). Однако определить, когда функции действительно чисты, может быть непросто.

## Состояние <a href="#id-8733" id="id-8733"></a>

Состояние — информация, к которой программа имеет доступ, и с которой может работать в определенный момент времени. Сюда входят данные, хранящиеся в памяти, порты ввода/вывода, базы данных и так далее. Например, содержимое переменных в приложении в любой данный момент времени репрезентативно для состояния приложения.

### С состоянием <a href="#id-5a86" id="id-5a86"></a>

Программы, приложения или компоненты с состоянием хранят в памяти данные о текущем состоянии. Они могут изменять состояние, а также имеют доступ к его истории. Следующий пример демонстрирует это:

```javascript
var number = 1;
function increment() {
  return number++;
}
// глобальная переменная изменяется: number = 2
increment();
```

### Без состояния <a href="#id-8f49" id="id-8f49"></a>

Функции или компоненты без состояния выполняют задачи, словно каждый раз их запускают впервые. Они не ссылаются или не используют в своем исполнении раннее созданные данные. Отсутствие состояния обеспечивает ссылочную прозрачность. Функции зависят только от их аргументов и не имеют доступа, не нуждаются в знании чего-либо вне области их видимости.[Чистые функции](https://ru.wikipedia.org/wiki/Чистота_функции) не имеют состояния. Пример:

```javascript
var number = 1;
function increment(n) {
  return n + 1;
}
// глобальная переменная НЕ изменяется: возвращает 2
increment(number);
```

Приложения без состояния все еще управляют состоянием. Однако они возвращают свое текущее состояние без изменения предыдущего состояния. Это принцип [функционального программирования](https://ru.wikipedia.org/wiki/Функциональное_программирование).

### Вывод

Управление состоянием важно для любого сложного приложения. Функции или компоненты с состоянием изменяют состояние и его историю, их труднее тестировать и отлаживать. Функции без состояния полагаются только на свои входные данные для создания данных выходных. Программа без состояния возвращает новое состояние, а не модифицирует существующее состояние.

## Неизменяемость и изменяемость <a href="#id-9783" id="id-9783"></a>

Концепции неизменяемости и изменяемости более туманны в JavaScript, чем в некоторых других языках программирования. Тем не менее, вы много услышите о неизменяемости при чтении о функциональном программировании в JS. Важно знать, что эти термины означают в классическом понимании и как они реализуются в JavaScript. Определения достаточно просты:

### Неизменяемый <a href="#id-9a76" id="id-9a76"></a>

Если объект является неизменяемым, его значение не может быть изменено после создания.

### Изменяемый <a href="#a379" id="a379"></a>

Если объект изменяем, его значение может быть изменено после создания.

### Реализация: неизменяемость и изменяемость в JavaScript <a href="#id-3f0b" id="id-3f0b"></a>

В JavaScript строки и числовые литералы реализованы неизменяемыми. Это легко понять, если рассмотреть, как мы работаем с ними:

```javascript
var str = 'Hello!';
var anotherStr = str.substring(2);
// результат: str = 'Hello!' (не изменена)
// результат: anotherStr = 'llo!' (новая строка)
```

Используя метод `.substring()`на нашем`Hello!`, строка не изменяет исходную строку. Вместо этого она создает новую строку. Мы могли бы переопределить значение переменной`str`на что-то другое, но, как только мы создали нашу строку`Hello!`, она навсегда останется`Hello!`

Числовые литералы также неизменяемы. Следующий пример всегда будет иметь одинаковый результат:

```javascript
var three = 1 + 2;
// результат: three = 3
```

Ни при каких обстоятельствах `1 + 2`не может стать чем-либо, кроме `3`.

Это демонстрирует, что в JavaScript присутствует реализация неизменяемости. Однако разработчики JS знают, что язык позволяет изменить многое. Например, объекты и массивы изменяемы. Рассмотрим следующий пример:

```javascript
var arr = [1, 2, 3];
arr.push(4);
// результат: arr = [1, 2, 3, 4]

var obj = { greeting: 'Hello' };
obj.name = 'Jon';
// результат: obj = { greeting: 'Hello', name: 'Jon' }
```

В этих примерах исходные объекты изменены. Новые объекты не возвращаются.

### Примеры

Функциональное программирование в JavaScript хорошо развивается. Но по своей сущности JS — очень изменчивый язык, состоящий из множества парадигм. Ключевая особенность функционального программирования — неизменяемость. Другие функциональные языки выбросят ошибку, когда разработчик попытается изменить неизменяемый объект. Тогда как мы можем примирить врожденную изменяемость JS при написании функционального или функционального реактивного JS?

Когда мы говорим о функциональном программировании в JS, слово «неизменяемое» используется много, но разработчик обязан всегда держать ее в голове. Например, [Redux полагается на одно неизменяемое](https://egghead.io/lessons/javascript-redux-the-single-immutable-state-tree) дерево состояний. Однако сам JavaScript способен изменять объект состояния. Чтобы реализовать неизменяемое дерево состояний, нам нужно каждый раз при изменении состояния [возвращать новый](https://egghead.io/lessons/javascript-redux-avoiding-array-mutations-with-concat-slice-and-spread)[объект состояния](https://egghead.io/lessons/javascript-redux-avoiding-object-mutations-with-object-assign-and-spread).

Для [неизменяемости](http://adripofjavascript.com/blog/drips/immutable-objects-with-object-freeze.html) объекты JavaScript также [могут быть заморожены](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) с помощью`Object.freeze(obj)`. Обратите внимание, что это "неглубокая" заморозка: значения объектов внутри замороженного объекта все еще могут быть изменены. Для гарантированной неизменяемости такие функции "глубокой" заморозки, как [Mozilla deepFreeze()](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) и [npm deep-freeze](https://www.npmjs.com/package/deep-freeze) могут рекурсивно замораживать объекты. Замораживание наиболее применимо в тестах, а не в приложении. Тесты будут оповещать разработчиков о возникновении изменений, чтобы их можно было исправить и избежать загромождающего`Object.freeze`в основном коде.

Существуют также библиотеки, поддерживающие неизменяемость в JS. [Mori](http://swannodette.github.io/mori/) предоставляет постоянные структуры данных на основе Clojure. [Immutable.js](https://facebook.github.io/immutable-js/) от Facebook также предоставляет [неизменяемые коллекции для JS](https://auth0.com/blog/intro-to-immutable-js/). Библиотеки утилит, такие как [Underscore.js](http://underscorejs.org/) и [lodash](http://www.lodash.com/), предоставляют методы и модули для более [функционального стиля программирования](https://github.com/lodash/lodash/wiki/FP-Guide) (стало быть, направленного на неизменяемость).

### Вывод

В целом, JavaScript — язык с сильной изменяемостью. Некоторые стили JS-кодирования опираются на эту врожденную изменяемость. Однако, при написании функционального JS, реализация неизменяемости требует внимательности. Если вы что-то нечаянно модифицируете, JS не будет выбрасывать ошибки. Тестирование и библиотеки могут помочь, но работа с неизменяемостью в JS требует практики и методологии.

Неизменяемость имеет свои преимущества. В результате получается код, который проще понимать. Он также обеспечивает [персистентность](https://neerc.ifmo.ru/wiki/index.php?title=Персистентные_структуры_данных), возможность хранения более старых версий структур данных и копирование только изменившихся частей.

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