Kuinka pisteettömä koostumus tekee sinusta paremman toiminnallisen ohjelmoijan

Discover Functional JavaScript nimettiin yhdeksi BookAuthorityn parhaista uusista funktionaalisen ohjelmoinnin kirjoista!

Kuva Hans-Peter Gauster on Unsplash
Pisteettömä tyyli - pyrkii vähentämään visuaalista sotkua poistamalla tarpeettomat parametriargumenttien kartoitukset.
Kyle Simpson toiminnallisen kevyen JavaScriptinä

Tarkastellaan juoksevaa koodia:

anna newBooks = Books.filter (point => isTechnology (point))

Katso nyt samaa koodia pisteiden (parametrit / argumentit) poistamisen jälkeen:

anna newBooks = Books.filter (isTechnology)

Pistettä luettelotoiminnoissa

Tehdään luettelotoiminnot pisteettömässä muodossa.

Oletetaan, että meidän on löydettävä tekniikanimikkeet kirjaluettelosta, valmistettava kirjaobjekti kaikilla tiedoilla näkymää varten ja lajiteltava kirjat tekijän nimen mukaan.

Koodi näyttää tältä:

toiminto getBooks () {
  palauta Books.filter (isTechnology)
              .map (toBookView)
              .sort (ascByAuthor);
}
// Pienet toiminnot pisteillä
toiminto ontekniikka (kirja) {
   palauta book.type === "T";
}
toimintoBookView (kirja) {
  palauta Object.freeze ({
    nimi: kirja.title,
    kirjoittaja: kirjoittajat [kirja.authorID] .name
  });
}
  
toiminto ascByAuthor (kirja1, kirja2) {
  if (kirja1.author  book2.author) palauttaa 1;
  paluu 0;
}

Soittopyynnöt isTechnology (), toBookView (), ascByAuthor () ovat pieniä toimintoja, joilla on tarkoitusta paljastavat nimet. Niitä ei ole rakennettu pisteettömään tyyliin.

Koodi, joka kokoaa kaikki nämä toiminnot getBooksissa (), on pistemätön.

Hajoaminen ja koostumus

Luonnollinen tapa käsitellä ongelmaa on hajottaa se pienemmiksi paloiksi ja koota sitten kaikki yhteen.

Jaotamme isomman tehtävän useisiin toimintoihin, jotka tekevät pienempiä tehtäviä. Sitten yhdistämme nämä pienemmät toiminnot uudelleen alkuperäisen ongelman ratkaisemiseksi.

Lukekaamme vaatimukset uudelleen:

Meidän on löydettävä tekniikanimikkeet kirjaluettelosta, valmistettava teoskohteen kaikki tiedot näkymää varten ja lajiteltava kirjat tekijän nimen mukaan.

Loimme:

  • isTechnology () predikaatti tarkistaa onko kyse teknisestä kirjasta
  • toViewBook ​​() rakentaa objektin kaikilla näkymää koskevilla tiedoilla
  • ascByAuthorname (), jos haluat lajitella kaksi kirjaa nousevaksi kirjoittajan nimen mukaan
  • getBooks () yhdistää kaikki nämä pienet toiminnot pisteettömässä muodossa
toiminto getBooks () {
  palauta Books.filter (isTechnology)
              .map (toBookView)
              .sort (ascByAuthor);
}

Askeleet kohti pistemätöntä koostumusta

Pisteettömässä sävellyksessä ei ole ylimääräistä anonyymiä takaisinsoittoa. Ei toiminto-avainsanaa, ei nuolen syntaksia =>. Ainoa mitä näemme ovat toimintonimet.

  • Useimmissa tapauksissa purataan nimettyjen toimintojen soittopyynnöt.
  • Yksinkertaisissa tapauksissa voit luoda takaisinsoiton lennossa vain työkalupakin apuohjelmalla. Katso esimerkiksi prop () -toimintoa.
  • Kirjoita koordinaattoritoiminto pisteettömässä muodossa.

Pienet toiminnot

Koodin kirjoittamisen seurauksena on paljon pieniä toimintoja, joiden tarkoituksena on paljastaa nimet. Näiden pienten toimintojen nimeäminen vaatii aikaa, mutta jos se onnistui, koodin lukeminen on helpompaa.

Toimintoja on kahdenlaisia:

  • Yhden tehtävän suorittavat toiminnot: ne ovat puhdasta tai sulkemistoimintoa. Yleensä niitä ei rakenneta pisteettömässä muodossa, vaan niillä on hyvät nimet.
  • Paljon tehtäviä koordinoivat toiminnot: Yhdistämällä nämä pienet tehtävät pisteettömässä muodossa on helpompi lukea.

Kaikki ei ole ilmaista

En pyrki siihen, että kaikessa ei olisi mitään. Tarkoitan pisteettömästi tietyissä paikoissa, etenkin säveltäessään toimintoja.

Osittainen soveltaminen

Osittainen soveltaminen viittaa prosessiin, jolla vahvistetaan funktio useista argumenteista. - lähde

Oletetaan esimerkiksi funktio isOfType (), joka voi suodattaa minkä tahansa tyyppisen kirjan mukaan. Katso alla olevaa koodia:

anna newBooks = kirjoja.suodatin (kirja => isBookOfType ("T", kirja));

Pisteettömänä koostumuksena reagoin nimettyyn funktioon ja säveltän sen sitten kaikki takaisin.

toiminto ontekniikka (kirja) {
  palauta isBookOfType ("T", kirja);
}
anna newBooks = kirjoja.suodatin (isTechnology);

Tässä tapauksessa voimme parantaa entistä paremmin ja luoda uuden predikaattifunktion pisteettömässä muodossa osittaista sovellusta käyttämällä.

anna isTechnology = isBookOfType.bind (nolla, "T");
anna newBooks = kirjoja.suodatin (isTechnology);

Pisteettömässä funktiossa määritelmän mukaan ei käytetä funktion avainsanaa tai => -merkkiä.

Predikaattifunktio on funktio, joka vie yhden kohteen syötteeksi ja palauttaa true / false sen perusteella, täyttääkö kohde ehdon. - lähde

Ilmaiseksi lupausten kanssa

Otetaan esimerkiksi tapa, jolla noudetaan joukko tehtävälomakkeita palvelimelta, valitaan tärkein ja esitetään ne sitten näytölle.

Alla on koodi:

fetchTodos (). sitten ((todos) => {
   anna topTodos = getTopPriority (todos);
   render (topTodos);
 }) .saalis ((virhe) => {
   console.error ("Oli virhe:" + virhe.status);
 });
toiminto getTopPriority (todos) {}
toiminnan renderöinti (todos) {}

Nyt refaktoroidaan se pisteettömään tyyliin. Tällä kertaa on jotain erilaista. Kuten näette, getTopPriority () -toiminnon tulosta käytetään renderöinti () -toiminnon tulona. Voimme käyttää lupausketjujärjestelmää yhdistämään getTopPriority () - ja render () -toiminnot toisiinsa.

Kuten aiemmin teimme, reagoimme palautuksen takaisinsoittoon nimetylle toiminnolle.

fetchTodos ()
  .Tämän jälkeen (getTopPriority)
  .Sitten (tee)
  .catch (handleError);
toimintokahvavirhe (virhe) {
  console.error ("Oli virhe:" + virhe.status);
}

Jokainen sitten () voi palauttaa arvon, jota se on käyttänyt sisääntulona seuraavalle () puhelulle.

Toolbox

Pisteettömässä muodossa tarvitsemme työkalupakin, joukon toimintoja, joita voidaan käyttää uudestaan ​​ja uudestaan ​​tavanomaisissa tilanteissa.

putki()

Otetaan tapaus, jos lähetän osan dataa useiden muunnoksien kautta.

Henkilöobjektissa, joissa on firstName ja lastName, molemmat nimet ovat ensin isoilla kirjaimilla. Sitten kokoNimi lasketaan. Sitten lopussa koko nimeä lyhennetään sopimaan näytön rajoituksiin.

Alla on koodi, joka näyttää kaikki nämä vaiheet:

toimintoFullNameForView (henkilö) {
  anna newPerson = capitalizeNames (henkilö);
  anna fullName = computeFullName (newPerson);
  palauta lyhyempi nimi (kokoNimi);
}
toiminto capitalizeNames (henkilö) {
  palauta {
   etunimi: isolla kirjaimella (henkilö.fi-nimi),
   sukunimi: iso kirjain (henkilö.sukunimi)
  }
}
toiminto isoilla (nimi) {
  paluunimi.charAt (0) .toUpperCase () + nimi.slice (1);
}
toiminto computeFullName (henkilö) {
  return person.firstName + "" + person.lastName;
}
toiminto lyhyempiNimi (nimi) {
  paluunimi.raita (0, 8);
}

capitalizeNames (), computeFullName (), lyhyempi nimi () ovat pieniä puhtaita funktioita. toFullNameForView () on koordinaattoritoiminto, joka säveltää ne yhdessä.

ToFullNameForView (): n määritteleminen pisteettömässä muodossa vaatii uuden apuohjelmatoiminnon: pipe ().

toimintoputki (... toimintoa) {
  paluutoiminto (arvo) {
   paluu function.reduce (composeLeftToRight, arvo);
  }
}
toiminto composeLeftToRight (computedValue, fn) {
  paluu fn (laskettu arvo);
}

putki () ketjut toimivat yhdessä siten, että kunkin toiminnon lähtöä käytetään seuraavan tulona.

Määritetään nyt koordinaattoritoiminto pisteettömässä muodossa.

anna toFullNameForView = pipe (
  capitalizeNames,
  computeFullName,
  shorterName,
);

prop ()

Otetaan seuraava yksinkertainen tapaus:

Books.map ((kirja) => book.title);

Yksi pisteettömänä vaihtoehtona on soittaa takaisin nimetylle toiminnolle:

anna otsikot = Books.map (toBookTitle);
toimintoBookTitle (kirja) {
  palauta book.title;
}

Toinen vaihtoehto on käyttää yleiskäyttöistä toimintoa, joka voi noutaa ominaisuuden: prop ().

anna otsikot = kirjoja.kartta (prop ("otsikko"));
toiminto prop (propName) {
  paluutoiminto getProp (obj) {
    return obj [propName];
  }
}

unary ()

unary () on funktiokoriste, joka ottaa funktion ja palauttaa funktion ottaen yhden parametrin. Sitä käytetään yleensä ongelmien korjaamiseen, kun funktiota kutsutaan enemmän argumenteilla kuin tarvitaan.

toiminto yhtenäinen (fn) {
  paluutoiminto unaryDecorator (ensimmäinen) {
    paluu fn.call (tämä ensimmäinen);
  }
}

Käytämme konsolia.log () pisteettömässä muodossa kaikkien numeroiden kirjaamiseen matriisista.

lasketaan numerot = [1,2,3,4,5,6];
numbers.forEach (console.log);
// 1 0 (6) [1, 2, 3, 4, 5, 6]
// 2 1 (6) [1, 2, 3, 4, 5, 6]
// ...

Kuten näette, siinä on ongelma. console.log () vastaanottaa lisää tarvittavia argumentteja. Meidän on varmistettava, että sitä kutsutaan vain yhdellä argumentilla.

Tässä on koodi, joka käyttää unary () -toimintoa.

numbers.forEach (unary (console.log));
// 1 2 3 4 5 6

Alla on toinen esimerkki, jossa käytetään parseInt (): tä pisteettömässä muodossa:

lasketaan numerot = [1,2,3,4,5,6];
numbers.map (parseInt); // [1, NaN, NaN, NaN, NaN, NaN]
numbers.map (unary (parseInt)); // [1, 2, 3, 4, 5, 6]

Ilmaiseksi menetelmillä

Käytämme nyt menetelmiä pisteettömässä muodossa. Luon objektin tehdastoiminnolla ja käytän sitten objektin menetelmää pisteettömässä muodossa.

Tässä on esimerkki:

toiminto Palvelu () {
  olkoon url = "http: //";
 
  toiminnon uudelleenlataus () {
    console.log (url);
  }
  
  palauta Object.freeze ({
    ladata
  });
}
var palvelu = uusi palvelu ();
setTimeout (service.reload); // http:

Menetelmän käyttäminen pisteettömässä muodossa toimii hyvin.

Seuraavaksi teen saman asian, mutta tällä kertaa rakennan objektin luokan avulla.

luokkapalvelu {
 rakentaja(){
    this.url = "http:";
  }
  
  lataa () {
    console.log (this.url);
  }
}
var palvelu = uusi palvelu ();
setTimeout (() => service.reload (), 0); // http:
setTimeout (service.reload, 0); // määrittelemätön

Kuten näette, tämä kadonnut konteksti.

Ongelma voidaan ratkaista käyttämällä esimerkiksi bind ().

setTimeout (service.reload.bind (service), 0);

Tämä on yksi syy, miksi suosin tehdastoimintoja luokkien sijasta. Tarkempi vertailu saadaan tutustumalla luokka vs. tehdas -toimintoon: tutkimalla etenemistapaa.

johtopäätös

Pisteettömä koostumus parantaa koodin selkeyttä ja luettavuutta.

Lisäksi se suosii käytäntöä hajottaa kaikki pienemmiksi kappaleiksi, jotka voidaan sitten säveltää erittäin ilmeisellä tavalla.

Pisteettömä tyyli kulkee käsi kädessä tahdon paljastavien nimien antamisen kanssa. Hyvien funktionimien kirjoittamiseen kuluva aika tekee pisteettömän sävellyksen lukemisen paljon helpommaksi.

Discover Functional JavaScript nimettiin yhdeksi BookAuthorityn parhaista uusista funktionaalisen ohjelmoinnin kirjoista!

Lisätietoja toiminnallisten ohjelmointitekniikoiden käytöstä Reaktissa on toiminnallisessa reagoinnissa.

Lue lisää Vue- ja Vuex-artikkeleista Vue.js-komponenttien lyhyestä johdannosta.

Opi soveltamaan suunnittelumallien periaatteita.

Löydät minut myös Twitteristä.