Чистые функции и побочные эффекты — хорошо известные концепции функционального программирования. Эти концепции также широко используются в JavaScript. Давай учить.

Введение в функции JavaScript

Функция позволяет нам логически размещать код для выполнения задачи. Functions отлично разбираются в языке программирования JavaScript. Вы можете создавать, изменять функцию, использовать ее в качестве аргумента другой функции или выполнять возврат из функции. Вы также можете присвоить функцию как значение переменной. Короче говоря, вы вряд ли будете использовать или писать какой-либо полезный код JavaScript без использования функций.

В этой статье мы узнаем о Pure Function, его преимуществах. Мы также рассмотрим Side Effects и их влияние.

Если вам также нравится учиться на видеоконтенте, эта статья также доступна в виде видеоурока здесь: 🙂

Пожалуйста, не стесняйтесь подписаться на будущий контент

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

Функция, явно возвращающая значение,

// Define the function
function testMe(input) {
    // Returns a string value
    return `testing ${input}`;
}
// Invoke the function
testMe(123); // returns 'testing 123'

Функция, не возвращающая значение явно,

// Define the function
function testMe() {
   // Do not return anything
}
// Invoke the function
testMe(); // returns undefined

Итак, разобравшись с основными способами использования, давайте рассмотрим сегодняшнюю тему Pure Function. Мы также поймем концепцию Side Effects и ее влияние на чистые функции.

Чистые функции и побочные эффекты с примерами

Как программист/разработчик программного обеспечения, вы пишете исходный код для получения вывода на основе входных данных. Обычно вы пишете functions для выполнения задач на основе входных данных и создания выходных данных. Нам нужно убедиться, что эти функции,

  • Предсказуемый: он дает предсказуемый результат для одних и тех же входных данных.
  • Читаемый: любой, кто читает функцию как отдельную единицу, может полностью понять ее назначение.
  • Многократное использование: можно повторно использовать функцию в нескольких местах исходного кода без изменения ее поведения и поведения вызывающего объекта.
  • Тестируемый: мы можем протестировать его как независимую единицу.

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

Приведенная ниже функция sayGreeting() является чистой функцией. Угадайте, почему?

function sayGreeting(name) {
  return `Hello ${name}`;
}

Это чистая функция, потому что вы всегда получаете Hello <name> в качестве вывода для прохода <name> в качестве ввода. Теперь давайте посмотрим на ту же функцию с небольшими изменениями.

let greeting = "Hello";
function sayGreeting(name) {
  return `${greeting} ${name}`;
}

Это чистая функция? Что ж, нет. Вывод функции теперь зависит от внешнего состояния с именем greeting. Что, если кто-то изменит значение переменной greeting на Hola? Это изменит вывод функции sayGreeting(), даже если вы передадите тот же ввод.

// When greeting is "Hello"
sayGreeting('Alex'); // Returns, "Hello Alex"
// When greeting is "Hola"
sayGreeting('Alex'); // Returns, "Hola Alex"

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

Еще несколько классических случаев побочных эффектов:

  • Мутация (изменение) самого ввода.
  • Запрос/обновление DOM
  • Ведение журнала (даже в консоли)
  • Выполнение вызова XHR/fetch.

Любая операция, не связанная напрямую с конечным результатом функции, называется Side Effect. Теперь давайте посмотрим на функцию impure, в которой мы изменяем входные данные и делаем то, чего не должны делать в чистой функции.

function findUser(users, item) {
    const reversedUsers = users.reverse();
    const found = reversedUsers.find((user) => {
        return user === item;
    });
   document.getElementById('user-found').innerText = found;
}

Приведенная выше функция принимает два аргумента: набор пользователей (массив) и элемент, который нужно найти в массиве. Он находит элемент с конца массива, переворачивая его. Как только элемент найден в массиве, он устанавливает это значение в виде текста в элемент HTML, используя методы DOM.

Здесь мы нарушаем два основных принципа pure function.

  1. Мы мутируем ввод.
  2. Мы запрашиваем и манипулируем DOM

Итак, какую проблему мы можем предвидеть? Давайте посмотрим. Вызывающий объект вызовет функцию findUser() следующим образом:

let users = ['Tapas', 'Alex', 'John', 'Maria'];
findUser(users, 'Maria');

На этом этапе вызывающая сторона может не знать, что функция выполняет операцию DOM, пока вызывающая сторона не прочитает код функции findUser(). Итак, readability скомпрометирован. Выход функции выполняет операцию, не связанную с конечным результатом.

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

function findUser(users, item) {
   // Create the clone of users array and then reverse
    const reversedUsers = [ ...users].reverse();
   // Find the element in the cloned array
    const found = reversedUsers.find((user) => {
        return user === item;
    });
   // Return the found element
    return found;
}

Затем,

let users = ['Tapas', 'Alex', 'John', 'Maria'];
let found = findUser(users, 'Maria');

Теперь функция findUser() является чистой функцией. Мы удалили побочные эффекты изменения ввода, и он возвращает предполагаемый результат. Следовательно, функция удобочитаема, тестируема как единое целое, многократно используется и предсказуема.

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

  • Ссылочная прозрачность: это означает, что мы должны иметь возможность заменить вызов функции (или вызов) ее выходным значением без изменения поведения программы. Как видите, это возможно, только если функция pure function.

Возьмем простую чистую функцию,

function multipication(x, y) {
 return x * y;
}

Итак, теперь в этом выражении мы можем заменить вызов функции ее выходным значением с гарантией отсутствия side effect,

10 + (multiplication(6, 3) ^ 2);

to,

10 + (18 ^ 2);
  • Параллельный код. Чистые функции помогают в параллельном выполнении кода. Однако в JavaScript код по умолчанию выполняется последовательно.

Итак, могу ли я сделать все функции Pure Functions?

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

Ваша прикладная программа будет иметь побочные эффекты, такие как вызовы HTTP, запись в консоль, операции ввода-вывода и многое другое. Пожалуйста, используйте чистые функции в максимально возможном количестве мест. Максимально изолируйте нечистые функции (побочные эффекты). Это значительно улучшит читабельность, отладку и тестируемость вашей программы.

Заключение

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

Если вы хотите глубже изучить эту тему и углубиться в функциональное программирование, возьмите эту книгу Кайла Симпсона Functional-Light JavaScript. Это стоит прочитать.

Давайте подключимся. Я также делюсь своими знаниями по JavaScript, веб-разработке и ведению блога на этих платформах.

Первоначально опубликовано на https://blog.greenroots.info.