Что такое чистая функция js
Перейти к содержимому

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

  • автор:

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

Что такое «чистые» и «нечистые» функции в JavaScript, и как их различать.

JavaScriptFunctional programming · 06.06.2019 · читать 3 мин �� · Автор: Alexey Myzgin

«Чистые» функции — это любые функции, исходные данные которых получены исключительно из их входных данных и не вызывают побочных эффектов в приложении. Математические функции являются примерами «чистых» функций. «Нечистые» функции бывают разных форм и размеров. Вот некоторые примеры:

  • функции, вывод которых зависит от внешнего / глобального состояния;
  • функции, которые возвращают разные исходные данные при одинаковых входных;
  • функции, которые изменяют состояние приложения;
  • функции, которые изменяют «внешний мир».

Функциональное программирование основано на использовании «чистых» функций и строгом контроле побочных эффектов. Способность распознавать любой тип функции является ключевым для функционального программирования.

«Чистая» функция — это функция, которая выводит свои данные основываясь исключительно на свои входные данные и не вызывает побочных эффектов в приложении.

Например у нас есть функция, которая получает одно значение x и возвращает в данном случае x + 1 :

// function f(x) const f = x => x + 1;

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

Первая «нечистая» функция

Первая «нечистая» функция, которую мы собираемся сделать — это та, чей результат не основан исключительно на её входных данных. Например, давай рассмотрим функцию totalPrice . У нас есть глобальная переменная — COST_OF_ITEM , которая содержит цену на товар. Функция totalPrice берет quantity и умножает ее на эту переменную.

// Глобальная переменная const COST_OF_ITEM = 250; const totalPrice = quantity => COST_OF_ITEM * quantity;

На первый взгляд может показаться, что это «чистая» функция, потому что мы всегда получаем один и тот же результат на основе одного и того же входного значения. Это можно увидеть, вызвав её несколько раз с одним и тем же значением, и вывив в консоль. В обоих случаях мы получаем 500.

// Глобальная переменная const COST_OF_ITEM = 250; const totalPrice = quantity => COST_OF_ITEM * quantity; console.log(totalPrice(2)); // 500 console.log(totalPrice(2)); // 500

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

const COST_OF_ITEM = 200; const totalPrice = quantity => COST_OF_ITEM * quantity; console.log(totalPrice(2)); // 400 console.log(totalPrice(2)); // 400

Вторая «нечистая» функция

Наш второй пример «нечистой» функции — это функция, которая получает один и тот же аргумент, но возвращает разные результаты. Часто в наших приложениях нам нужно создавать объекты, которые имеют уникальные идентификаторы, такие как id .

Давай создадим функцию generateID . Эта функция будет возвращать целое число в случайном порядке. Это «нечистая» функция, так как вызвав её несколько раз, каждый раз мы получим разный результат.

const generateID = () => Math.floor(Math.random() * 10000); console.log(generateID()); // 7602 console.log(generateID()); // 1377 console.log(generateID()); // 7131

Давай используем нашу «нечистую» функцию generateID внутри фабричной функции для создания пользовательских объектов. Чтобы создать пользователя, createUser принимает два параметра: name и age , и возвращает объект с id , используя функцию generateID для его создания, а также name и age .

const generateID = () => Math.floor(Math.random() * 10000); const createUser = (name, age) => ( id: generateID(), name, age >);

Вызовем createUser несколько раз, с одинаковыми аргументами.

const generateID = () => Math.floor(Math.random() * 10000); const createUser = (name, age) => ( id: generateID(), name, age >); console.log(createUser("Alex", 28)); // console.log(createUser("Alex", 28)); // console.log(createUser("Alex", 28)); // 

Если посмотрим в консоль, то увидим, что мы получили похожие объекты, но они не одинаковые — id у всех разный.

«Нечистота» функции generateID делает нашу фабричную функцию createUser «нечистой». Для того чтобы исправить это — можно переместить «нечистую» функцию за пределы фабрики и вызвать её где-нибудь, где мы ожидаем побочный эффект, и передать id в качестве параметра в нашу фабрику createUser .

Третья «нечистая» функция

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

Допустим, мы отслеживаем изменяемое значение (в данном случае id ). Если мы создадим функцию, которая изменяет это значение, у нас будет «нечистая» функция. Например фабричная функция createPersone .

let id = 0; const createPersone = name => ( id: ++id, name >);

Если мы генерируем наш id для этого объекта, изменяя значение глобального id , то это «нечистая» функция. Вызвав эту функции несколько раз с разными name , то увидим, что id увеличился как мы и ожидали, но если мы также выведем в консоль глобальное значение id , то увидим, что оно тоже изменилось.

let id = 0; const createPersone = name => ( id: ++id, name >); console.log(createPersone("Alex")); // console.log(createPersone("Julia")); // console.log(id); // 2

Четвертая «нечистая» функция

Последний четвертый пример «нечистой» функции — это побочный эффект “внешнего мира”. console.log — «нечистая» функция, так как она создает побочный эффект во “внешнем мире”.

Каждый раз, когда мы используем console.log , это влияет на нашу консоль, а это побочный эффект. Если у нас есть какая-либо функция, использующая console.log , (например, функция logger , которая принимает сообщение и выводит его) это тоже «нечистая» функция.

const logger = msg =>  console.log(msg); >; logger("Всем привет!");

JavaScript: что такое чистые функции и зачем их использовать?

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

Когда я впервые услышал термин «чистая функция», я был сбит с толку. Что не так с штатной функцией? Почему он должен быть чистым? Зачем мне вообще нужны чистые функции?

Если вы еще не знаете, что такое чистые функции, вы, вероятно, задаете те же вопросы. На самом деле они действительно простые. Позволь мне показать тебе …

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

Определение чистой функции:

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

Прежде чем я покажу вам пример чистой и нечистой функции, давайте обсудим ужасные «побочные эффекты».

Какие наблюдаемые побочные эффекты?

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

Примечание. Если чистая функция вызывает чистую функцию, это не является побочным эффектом, и вызывающая функция остается чистой.

Побочные эффекты включают, но не ограничиваются:

  • Выполнение HTTP-запроса
  • Мутирующие данные
  • Печать на экран или консоль
  • DOM-запрос / манипуляции
  • Math.random ()
  • Получение текущего времени

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

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

В демонстрационных целях приведен пример чистой функции, которая вычисляет цену продукта, включая налог (налог в Великобритании составляет 20%):

function priceAfterTax(productPrice)

Если вы запустите эту функцию с одним и тем же входом 100000000 раз, она всегда будет давать один и тот же результат .

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

Я показал вам чистую функцию, теперь давайте посмотрим на пример нечистой функции в JavaScript:

var tax = 20; function calculateTax(productPrice) < return (productPrice * (tax/100)) + productPrice; >

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

Почему чистые функции важны в JavaScript?

Чистые функции широко используются в функциональном программировании. А библиотеки, такие как ReactJS и Redux, требуют использования чистых функций (ps. Если вы не знаете, что ReactJS изучите его. Это изменит вашу жизнь).

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

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

Тестируемость и рефакторинг

Еще одна причина по возможности использовать чистые функции — это тестирование и рефакторинг.

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

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

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

Обратите внимание, что чистые функции не ограничиваются JavaScript. Более подробное — ошеломляющее — объяснение чистых функций см. Здесь . Я также очень рекомендую прочитать это и это .

Что такое чистые функции в JavaScript?

Чистые функции — строительные блоки в функциональном программировании. Их обожают за простоту и тестируемость.

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

image

Чек-лист

Функция должна удовлетворять двум условиям, чтобы считаться «чистой»:

— Каждый раз функция возвращает одинаковый результат, когда она вызывается с тем же набором аргументов

— Нет побочных эффектов

1. Одинаковый вход => Одинаковый выход

const add = (x, y) => x + y; add(2, 4); // 6
let x = 2; const add = (y) => < x += y; >; add(4); // x === 6 (the first time)

В первом случае значение возвращается на основании заданных параметров, независимо от того, где/когда вы его вызываете.

Если вы сложите 2 и 4, всегда получите 6.

Ничего не влияет на результат.

Нечистые функции = непостоянные результаты

Второй пример ничего не возвращает. Он полагается на общее состояние для выполнения своей работы путем увеличения переменной за пределами своей области.

Эта модель кошмар для разработчиков.

Разделяемое состояние вводит зависимость от времени. Вы получаете разные результаты в зависимости от того, когда вы вызвали функцию. В первый раз результат 6, в следующий раз 10 и так далее.

В каком случае вы получите меньше багов, которые появляются только при определенных условиях?

В каком случае с большей вероятностью вы преуспеете в многопоточной среде, где временные зависимости могут сломать систему?

Определенно в первом.

2. Нет побочных эффектов

image

Этот тест сам по себе контрольный список.

Примеры побочных эффектов:

  1. Видоизменение входных параметров
  2. console.log
  3. HTTP вызовы (AJAX/fetch)
  4. Изменение в файловой системе
  5. Запросы DOM

Советую посмотреть видео Боба Мартина.

Вот “нечистая” функция с побочным эффектом.

const impureDouble = (x) => < console.log('doubling', x); return x * 2; >; const result = impureDouble(4); console.log(< result >);

console.log здесь это побочный эффект, но он не повредит. Мы все равно получим те же результаты, учитывая те же данные.

Однако, это может вызвать проблемы.

“Нечистое” изменение объекта

const impureAssoc = (key, value, object) => < object[key] = value; >; const person = < name: 'Bobo' >; const result = impureAssoc('shoeSize', 400, person); console.log(< person, result >);

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

Разделяемое состояние означает, что влияние impureAssoc уже не полностью очевидно. Понимание влияния на систему теперь включает отслеживание каждой переменной, к которой когда-либо прикасалась, и знание ее истории.

Разделяемое состояние = временные зависимости.

Мы можем очистить impureAssoc, просто вернув новый объект с желаемыми свойствами.

“Очищаем это”

const pureAssoc = (key, value, object) => (< . object, [key]: value >); const person = < name: 'Bobo' >; const result = pureAssoc('shoeSize', 400, person); console.log(< person, result >);

Теперь pureAssoc возвращает тестируемый результат, и можно не беспокоиться, если он изменится где-то в другом месте.

Можно было сделать и так:

const pureAssoc = (key, value, object) => < const newObject = < . object >; newObject[key] = value; return newObject; >; const person = < name: 'Bobo' >; const result = pureAssoc('shoeSize', 400, person); console.log(< person, result >);

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

Изменения ограничиваются этой небольшой областью, и вы все еще возвращаете значение.

Резюме

  • Функция чистая, если не имеет побочных эффектов и каждый раз возвращает одинаковый результат, когда она вызывается с тем же набором аргументов.
  • Побочные эффекты включают: меняющийся вход, HTTP-вызовы, запись на диск, вывод на экран.
  • Вы можете безопасно клонировать, а затем менять входные параметры. Просто оставьте оригинал без изменений.
  • Синтаксис распространения (… syntax) — это самый простой способ клонирования объектов и массивов.

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

Что такое «чистые» и «нечистые» функции в JavaScript, и как их различать.

JavaScriptFunctional programming · 06.06.2019 · читать 3 мин �� · Автор: Alexey Myzgin

«Чистые» функции — это любые функции, исходные данные которых получены исключительно из их входных данных и не вызывают побочных эффектов в приложении. Математические функции являются примерами «чистых» функций. «Нечистые» функции бывают разных форм и размеров. Вот некоторые примеры:

  • функции, вывод которых зависит от внешнего / глобального состояния;
  • функции, которые возвращают разные исходные данные при одинаковых входных;
  • функции, которые изменяют состояние приложения;
  • функции, которые изменяют «внешний мир».

Функциональное программирование основано на использовании «чистых» функций и строгом контроле побочных эффектов. Способность распознавать любой тип функции является ключевым для функционального программирования.

«Чистая» функция — это функция, которая выводит свои данные основываясь исключительно на свои входные данные и не вызывает побочных эффектов в приложении.

Например у нас есть функция, которая получает одно значение x и возвращает в данном случае x + 1 :

// function f(x) const f = x => x + 1;

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

Первая «нечистая» функция

Первая «нечистая» функция, которую мы собираемся сделать — это та, чей результат не основан исключительно на её входных данных. Например, давай рассмотрим функцию totalPrice . У нас есть глобальная переменная — COST_OF_ITEM , которая содержит цену на товар. Функция totalPrice берет quantity и умножает ее на эту переменную.

// Глобальная переменная const COST_OF_ITEM = 250; const totalPrice = quantity => COST_OF_ITEM * quantity;

На первый взгляд может показаться, что это «чистая» функция, потому что мы всегда получаем один и тот же результат на основе одного и того же входного значения. Это можно увидеть, вызвав её несколько раз с одним и тем же значением, и вывив в консоль. В обоих случаях мы получаем 500.

// Глобальная переменная const COST_OF_ITEM = 250; const totalPrice = quantity => COST_OF_ITEM * quantity; console.log(totalPrice(2)); // 500 console.log(totalPrice(2)); // 500

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

const COST_OF_ITEM = 200; const totalPrice = quantity => COST_OF_ITEM * quantity; console.log(totalPrice(2)); // 400 console.log(totalPrice(2)); // 400

Вторая «нечистая» функция

Наш второй пример «нечистой» функции — это функция, которая получает один и тот же аргумент, но возвращает разные результаты. Часто в наших приложениях нам нужно создавать объекты, которые имеют уникальные идентификаторы, такие как id .

Давай создадим функцию generateID . Эта функция будет возвращать целое число в случайном порядке. Это «нечистая» функция, так как вызвав её несколько раз, каждый раз мы получим разный результат.

const generateID = () => Math.floor(Math.random() * 10000); console.log(generateID()); // 7602 console.log(generateID()); // 1377 console.log(generateID()); // 7131

Давай используем нашу «нечистую» функцию generateID внутри фабричной функции для создания пользовательских объектов. Чтобы создать пользователя, createUser принимает два параметра: name и age , и возвращает объект с id , используя функцию generateID для его создания, а также name и age .

const generateID = () => Math.floor(Math.random() * 10000); const createUser = (name, age) => ( id: generateID(), name, age >);

Вызовем createUser несколько раз, с одинаковыми аргументами.

const generateID = () => Math.floor(Math.random() * 10000); const createUser = (name, age) => ( id: generateID(), name, age >); console.log(createUser("Alex", 28)); // console.log(createUser("Alex", 28)); // console.log(createUser("Alex", 28)); // 

Если посмотрим в консоль, то увидим, что мы получили похожие объекты, но они не одинаковые — id у всех разный.

«Нечистота» функции generateID делает нашу фабричную функцию createUser «нечистой». Для того чтобы исправить это — можно переместить «нечистую» функцию за пределы фабрики и вызвать её где-нибудь, где мы ожидаем побочный эффект, и передать id в качестве параметра в нашу фабрику createUser .

Третья «нечистая» функция

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

Допустим, мы отслеживаем изменяемое значение (в данном случае id ). Если мы создадим функцию, которая изменяет это значение, у нас будет «нечистая» функция. Например фабричная функция createPersone .

let id = 0; const createPersone = name => ( id: ++id, name >);

Если мы генерируем наш id для этого объекта, изменяя значение глобального id , то это «нечистая» функция. Вызвав эту функции несколько раз с разными name , то увидим, что id увеличился как мы и ожидали, но если мы также выведем в консоль глобальное значение id , то увидим, что оно тоже изменилось.

let id = 0; const createPersone = name => ( id: ++id, name >); console.log(createPersone("Alex")); // console.log(createPersone("Julia")); // console.log(id); // 2

Четвертая «нечистая» функция

Последний четвертый пример «нечистой» функции — это побочный эффект “внешнего мира”. console.log — «нечистая» функция, так как она создает побочный эффект во “внешнем мире”.

Каждый раз, когда мы используем console.log , это влияет на нашу консоль, а это побочный эффект. Если у нас есть какая-либо функция, использующая console.log , (например, функция logger , которая принимает сообщение и выводит его) это тоже «нечистая» функция.

const logger = msg =>  console.log(msg); >; logger("Всем привет!");

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *