Какво е чиста функция в JavaScript?

Чистите функции са атомните градивни елементи във функционалното програмиране. Те са обожавани заради тяхната простота и проверимост.

Тази публикация обхваща бърз контролен списък, за да разбере дали дадена функция е чиста или не.

Контролният списък

Функцията трябва да премине два теста, за да се счита за „чиста“:

  1. Едни и същи входове винаги връщат едни и същи изходи
  2. Няма странични ефекти

Нека да увеличим всеки от тях.

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. Без странични ефекти

Този тест сам по себе си е контролен списък. Няколко примера за странични ефекти са

  1. Мутиране на вашия вход
  2. console.log
  3. HTTP повиквания (AJAX / извличане)
  4. Промяна на файловата система (fs)
  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 }); 

Мутирането на вашия вход може да бъде опасно, но мутирането на негово копие не е проблем. Нашият краен резултат все още е проверима, предсказуема функция, която работи независимо къде и кога я извикате.

Мутацията е ограничена до този малък обхват и вие все още връщате стойност.

Дълбоко клониране на обекти

Внимание! Използването на оператора за разпространение ...създава плитко копие на обект. Плитките копия не са защитени от вложени мутации.

Благодаря ти Родриго Фернандес Диас, че ми обърна внимание на това!

Неопасна вложена мутация

const person = { name: 'Bobo', address: { street: 'Main Street', number: 123 } }; const shallowPersonClone = { ...person }; shallowPersonClone.address.number = 456; console.log({ person, shallowPersonClone }); 

И двамата personи shallowPersonCloneбяха мутирали, защото децата им споделят една и съща справка!

Безопасна вложена мутация

To safely mutate nested properties, we need a deep clone.

const person = { name: 'Bobo', address: { street: 'Main Street', number: 123 } }; const deepPersonClone = JSON.parse(JSON.stringify(person)); deepPersonClone.address.number = 456; console.log({ person, deepPersonClone }); 

Now you’re guaranteed safety because they’re truly two separate entities!

Summary

  • A function’s pure if it’s free from side-effects and returns the same output, given the same input.
  • Side-effects include: mutating input, HTTP calls, writing to disk, printing to the screen.
  • You can safely clone, thenmutate, your input. Just leave the original one untouched.
  • Spread syntax ( syntax) is the easiest way to shallowly clone objects.
  • JSON.parse(JSON.stringify(object)) is the easiest way to deeply clone objects. Thanks again Rodrigo Fernández Díaz!

My Free Course

This tutorial was from my completely free course on Educative.io, Functional Programming Patterns With RamdaJS!

Please consider taking/sharing it if you enjoyed this content.

It’s full of lessons, graphics, exercises, and runnable code samples to teach you a basic functional programming style using RamdaJS.

Thanks for reading! Until next time.