Mi a tiszta funkció a JavaScript-ben?

A tiszta funkciók az atom építőkövei a funkcionális programozásnak. Imádják őket egyszerűségük és tesztelhetőségük miatt.

Ez a bejegyzés egy gyors ellenőrzőlistát tartalmaz, amelyből kiderül, hogy a függvény tiszta-e vagy sem.

Az ellenőrzőlista

Egy funkciónak két tesztet kell teljesítenie ahhoz, hogy „tiszta” legyen:

  1. Ugyanazok a bemenetek mindig ugyanazokat a kimeneteket adják vissza
  2. Nincs mellékhatása

Nagyítsunk mindegyikre.

1. Ugyanaz a bemenet => Ugyanaz a kimenet

Hasonlítsa össze ezt:

const add = (x, y) => x + y; add(2, 4); // 6 

Ehhez:

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

Tiszta funkciók = következetes eredmények

Az első példa egy értéket ad vissza a megadott paraméterek alapján, függetlenül attól, hogy hol / mikor hívja.

Ha elhaladsz 2és 4mindig kapsz 6.

Semmi más nem befolyásolja a kimenetet.

Szennyeződési funkciók = következetlen eredmények

A második példa nem ad eredményt. A megosztott állapotra támaszkodik, hogy munkáját úgy végezze, hogy a saját hatókörén kívüli változót növel.

Ez a minta egy fejlesztő rémálma üzemanyag.

A megosztott állapot időfüggést vezet be. Különböző eredményeket kap attól függően, hogy mikor hívta meg a függvényt. Az első eredmény azt eredményezi 6, a következő 10és így tovább.

Melyik verzió könnyebben érvel?

Melyik ritkább olyan hibákat tenyészteni, amelyek csak bizonyos körülmények között fordulnak elő?

Melyik nagyobb valószínűséggel fog sikert elérni egy több szálat tartalmazó környezetben, ahol az időfüggőségek megbontják a rendszert?

Mindenképpen az első.

2. Nincsenek mellékhatások

Ez a teszt maga egy ellenőrzőlista. Néhány példa a mellékhatásokra

  1. A bemenet némítása
  2. console.log
  3. HTTP hívások (AJAX / letöltés)
  4. A fájlrendszer módosítása (fs)
  5. A DOM lekérdezése

Alapvetően minden olyan funkció, amelyet egy függvény végez, amely nem kapcsolódik a végső kimenet kiszámításához.

Itt egy tisztátalan funkció, mellékhatással.

Nem olyan rossz

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

console.logitt a mellékhatás, de minden gyakorlatban nem árt nekünk. Még mindig ugyanazokat a kimeneteket kapjuk, ugyanazokat a bemeneteket kapva.

Ez azonban problémát okozhat.

Objektum „piszkos” módosítása

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

A (z) változó personörökre megváltozott, mert a függvényünk hozzárendelési utasítást vezetett be.

A megosztott állapot azt jelenti impureAssoc, hogy a hatása már nem teljesen nyilvánvaló. A rendszerre gyakorolt ​​hatásának megértése magában foglalja minden olyan változó felkutatását, amelyet valaha érintett, és ismeri azok történetét.

Megosztott állapot = időzítési függőségek.

Tisztíthatunk úgy, impureAssochogy egyszerűen visszaküldünk egy új objektumot a kívánt tulajdonságokkal.

Megtisztítása

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

Most pureAssoctesztelhető eredményt ad, és soha nem fogunk aggódni, ha az csendesen mutál valamit másutt.

Akár a következőket teheti, és tiszta maradhat:

Egy másik tiszta út

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 }); 

A bemenet némítása veszélyes lehet, de annak másolatának mutációja nem jelent problémát. Végeredményünk még mindig tesztelhető, kiszámítható funkció, amely bárhonnan / mikor is hívja.

A mutáció erre a kis terjedelemre korlátozódik, és még mindig visszaad egy értéket.

Objektumok mély klónozása

Fel a fejjel! A spread operátor használatával ...létrehoz egy objektum sekély másolatát. A sekély másolatok nem biztonságosak a beágyazott mutációktól.

Köszönöm Rodrigo Fernández Díaz, hogy felhívta erre a figyelmemet!

Nem biztonságos beágyazott mutáció

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

Mindkettő, personés shallowPersonCloneazért is mutáltak, mert gyermekeik ugyanazon hivatkozásban részesülnek!

Biztonságos beágyazott mutáció

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.