JavaScript: Czym jest czysta funkcja?

Opublikowano: 16.08.2022 - tagi: JavaScript Funkcja

Czym to jest czysta funkcja?

Czysta funkcja (ang.: pure function) jest to termin z programowania funkcjonalnego, który mówi, że funkcja jest "czysta" jeśli są spełnione następujące warunki:

  1. Te same dane przekazane do funkcji zawsze dają taki sam wynik.
  2. Funkcja nie tworzy efektów ubocznych.

Przyjrzyjmy się obu warunkom bliżej.

Dane na wejściu, wynik na wyjściu

Te same dane przekazane do funkcji zawsze dają taki sam wynik.

Czyli: dla taki samych parametrów funkcja zawsze powinna zwrócić ten sam wynik:

const multiply = (num, by) => num * by;

console.log(multiply(2, 3)); // 6
console.log(multiply(2, 4)); // 8
console.log(multiply(2, 3)); // 6

Podajemy dwa razy te same parametry: 2 i 3 i dostajemy ten sam wynik: 6. To jest czysta funkcja.

Inny przykład:

console.log(Math.random()); // 0.2755161550452777
console.log(Math.random()); // 0.5888948222087572
console.log(Math.random()); // 0.2976254575123847

To nie jest czysta funkcja! Dla tych samych danych wejściowych dostajemy różne wyniki. Pierwszy warunek nie został spełniony w tym przypadku.

Efekty uboczne

Funkcja nie tworzy efektów ubocznych.

To znaczy, że czysta funkcja nie zmienia stanu danych do niej przekazanych.

Przykład 1

const setProperty = (property, value, object) => {
  object[property] = value;
}

const obj = { name: 'Object' };
setProperty('size', 100, obj); // { name: 'Object', size: 100 }

console.log(obj);

Funkcja setProperty nie można nazwać czystą, ponieważ tworzy efekt uboczny. Zmienia stan obiektu, który jest przekazany do funkcji.

Poprawiona wersja:

const setProperty = (property, value, object) => {
	return ({
		...object,
		[property]: value
	});
}

const obj = { name: 'Object' };
const updatedObject = setProperty('size', 100, obj);

console.log(obj); // { name: 'Object' }
console.log(updatedObject); // { name: 'Object', size: 100 }

Przykład 2

Czysta funkcja nie polega na stanie z zewnątrz:

const counterValue = 10;
const updateCounter = (value) => value + counterValue;

console.log(updateCounter(5)); // 15
console.log(updateCounter(10)); // 20
console.log(updateCounter(10)); // 20

Tutaj niby wszystko jest ok. Daje takie same wyniki na przykład dla parametru 10. Problem jest taki, że działamy na zmiennej z zewnątrz.

A co jeśli zmienimy nieco kod:

let counterValue = 10;
const updateCounter = (value) => value + counterValue;

console.log(updateCounter(5)); // 15
console.log(updateCounter(10)); // 20
counterValue += 15;
console.log(updateCounter(10)); // 35

Ups... mamy już inne wyniki...