Toiminnallisen ohjelmoijan johdanto JavaScript-ohjelmaan (säveltämisohjelmisto)

Tupakointikuutiot savuksi - MattysFlicks - (CC BY 2.0)
Huomaa: Tämä on osa ”Composing Software” -sarjaa (nyt kirja!), Joka käsittelee toiminnallisen ohjelmoinnin ja koostumustekniikan tekniikoiden käyttöä JavaScriptES6 +: ssa alusta alkaen. Pysy kanavalla. Tulevaa on paljon enemmän!
Osta kirja | Hakemisto |

Niille, jotka eivät tunne JavaScriptiä tai ES6 +: ta, tämä on tarkoitettu lyhyt johdanto. Olet sitten aloittelija tai kokenut JavaScriptin kehittäjä, saatat oppia jotain uutta. Seuraava on tarkoitettu vain naarmuttamaan pintaa ja saamaan sinut innostumaan. Jos haluat tietää enemmän, sinun on vain tutkittava syvemmälle. Edessä on paljon enemmän.

Paras tapa oppia koodaamaan on koodata. Suosittelen, että seuraat interaktiivisen JavaScript-ohjelmointiympäristön, kuten CodePenin tai Babel REPL: n, käyttöä.

Vaihtoehtoisesti voit päästä eroon käyttämällä Solmua tai selainkonsolin VASTAUKSIA.

Lausekkeet ja arvot

Lauseke on koodinpätkä, joka arvioi arvoon.

Seuraavat ovat kaikki kelvollisia lausekkeita JavaScript:

7;
7 + 1; // 8
7 * 2; // 14
'Hei'; // Hei

Lausekkeen arvolle voidaan antaa nimi. Kun teet niin, lauseke arvioidaan ensin ja tuloksena oleva arvo osoitetaan nimelle. Käytämme tässä const-avainsanaa. Se ei ole ainoa tapa, mutta sitä käytetään eniten, joten pysymme nyt constissa:

const hello = 'Hei';
Hei; // Hei

var, anna ja const

JavaScript tukee kahta muuta muuttuvaa ilmaisuavainsanaa: var ja let. Haluan ajatella niitä valintajärjestyksessä. Olen oletuksena valinnut tiukimman ilmoituksen: const. Const-avainsanalla ilmoitettua muuttujaa ei voida osoittaa uudelleen. Lopullinen arvo on annettava ilmoituksen tekohetkellä. Tämä voi kuulostaa jäykältä, mutta rajoitus on hyvä asia. Se on signaali, joka kertoo sinulle, että "tälle nimelle annettu arvo ei muutu". Se auttaa sinua ymmärtämään täysin, mitä nimi tarkoittaa heti, joutumatta lukemaan koko toimintoa tai estämään laajuutta.

Joskus on hyödyllistä määrittää muuttujat uudelleen. Jos esimerkiksi käytät manuaalista, pakollista iteraatiota toiminnallisemman lähestymistavan sijasta, voit iteroida laskurilla määritetyn laskurin.

Koska var kertoo vähiten muuttujasta, se on heikoin signaali. Siitä lähtien kun aloin käyttää ES6: ta, en ole koskaan tarkoituksella julistanut var-ohjelmaa oikeassa ohjelmistoprojektissa.

Huomaa, että kun muuttuja julistetaan arvolla tai constilla, kaikki yritykset julistaa se uudelleen johtavat virheeseen. Jos haluat enemmän kokeellista joustavuutta REPL (Lue, Eval, Tulosta silmukka) -ympäristössä, voit käyttää muuttujien ilmoittamiseen const: n sijasta var. Var: n uudelleenlaskenta on sallittu.

Tässä tekstissä käytetään constia saadaksesi tapansa laiminlyödä valintaa todellisista ohjelmista, mutta voit vapaasti korvata var: n vuorovaikutteisen kokeilun tarkoituksiin.

Tyypit

Toistaiseksi olemme nähneet kahta tyyppiä: numeroita ja merkkijonoja. JavaScriptillä on myös boolean (tosi tai epätosi), taulukot, objektit ja paljon muuta. Pääset muihin tyyppeihin myöhemmin.

Matriisi on tilattu luettelo arvoista. Ajattele sitä laatikkona, johon mahtuu monia esineitä. Tässä on taulukon kirjaimellinen merkintä:

[1, 2, 3];

Tietysti se on lauseke, jolle voidaan antaa nimi:

const arr = [1, 2, 3];

Kohde JavaScript-muodossa on kokoelma avain: arvo-pareja. Sillä on myös kirjaimellinen merkintä:

{
  avain: 'arvo'
}

Ja tietysti, voit antaa objektin nimen:

const foo = {
  bar: 'bar'
}

Jos haluat määrittää olemassa olevat muuttujat objektin ominaisuuden avaimiin samalla nimellä, siihen on linkki. Voit kirjoittaa vain muuttujan nimen sen sijaan, että annat sekä avaimen että arvon:

const a = 'a';
const oldA = {a: a}; // pitkä, tarpeeton tapa
const oA = {a}; // lyhyt makea!

Tehdään se vain hauskanpitoon uudelleen:

const b = 'b';
const oB = {b};

Objektit voidaan helposti yhdistää uusiksi objekteiksi:

const c = {... oA, ... oB}; // {a: 'a', b: 'b'}

Ne pisteet ovat objektin hajautuksen operaattori. Se toistaa oA: n ominaisuudet ja määrittää ne uudelle objektille, sitten tekee saman oB: lle, ohittaen kaikki avaimet, jotka jo ovat uudessa objektissa. Tästä kirjoituksesta alkaen objektihajautus on uusi kokeellinen ominaisuus, jota ei ehkä vielä ole saatavana kaikissa suosituissa selaimissa, mutta jos se ei toimi sinulle, sillä on korvike: Object.assign ():

const d = Object.assign ({}, oA, oB); // {a: 'a', b: 'b'}

Vain vähän enemmän kirjoittamista Object.assign () -esimerkissä. Jos kirjoitat paljon objekteja, se saattaa jopa säästää kirjoittamista. Huomaa, että kun käytät Object.assign () -kohtaa, sinun on läpäistävä kohdeobjekti ensimmäisenä parametrina. Ominaisuudet kopioidaan kohteeseen. Jos unohdat ja jätät kohdeobjektin, ensimmäisessä argumentissa kuljettu objekti mutatoituu.

Kokemukseni mukaan olemassa olevan objektin mykistäminen uuden objektin luomisen sijaan on yleensä virhe. Ainakin se on virhealtista. Ole varovainen Object.assign () -sovelluksen kanssa.

destrukturointiainetta

Sekä objektit että taulukot tukevat hajotusta, mikä tarkoittaa, että voit poimia niistä arvoja ja määrittää ne nimetyille muuttujille:

const [t, u] = ['a', 'b'];
t; // 'a'
u; // 'b'
const blep = {
  blop: 'blop'
};

// Seuraava vastaa:
// const blop = blep.blop;
const {blop} = räpätä;
blop; // 'esto'

Kuten yllä olevassa taulukkoesimerkissä, voit hajottaa useita tehtäviä kerralla. Tässä on linja, jonka näet monissa Redux-projekteissa:

const {tyyppi, hyötykuorma} = toiminta;

Näin sitä käytetään reduktorin yhteydessä (paljon lisää aiheesta myöhemmin):

const myReducer = (tila = {}, toiminta = {}) => {
  const {tyyppi, hyötykuorma} = toiminta;
  kytkin (tyyppi) {
    tapaus 'FOO': palauta Object.assign ({}, tila, hyötykuorma);
    oletus: palautustila;
  }
};

Jos et halua käyttää toista nimeä uudessa sidoksessa, voit antaa uuden nimen:

const {blop: bloop} = räpätä;
bloop; // 'esto'

Lue: Määritä blep.blop bloopiksi.

Vertailut ja ternäärät

Voit verrata arvoja tiukan tasa-arvon operaattoriin (jota joskus kutsutaan ”kolminkertaiseksi”):

3 + 1 === 4; // totta

Siellä on myös huolimaton tasa-arvooperaattori. Sitä kutsutaan virallisesti nimellä "Equal" -operaattori. Epävirallisesti "kaksinkertainen yhtä suuri". Tupla-yhtälöillä on kelvollinen käyttötapaus tai kaksi, mutta melkein aina parempi on olettaa ===-operaattorille.

Muita vertailuoperaattoreita ovat:

  • > Suurempi kuin
  • > = Suurempi tai yhtä suuri kuin
  • <= Pienempi tai yhtä suuri kuin
  • ! = Ei yhtä suuri
  • ! == Ei tiukasti yhtä
  • && Logical ja
  • || Looginen tai

Kolmiosainen lauseke on lauseke, jonka avulla voit kysyä kysymystä vertailijan avulla ja arvioida erilaiseen vastaukseen sen mukaan, onko lauseke totuus vai ei:

14 - 7 === 7? 'Jep!' : 'Ei!'; // Jep!

tehtävät

JavaScriptillä on funktiolausekkeita, jotka voidaan määrittää nimille:

const double = x => x * 2;

Tämä tarkoittaa samaa kuin matemaattinen funktio f (x) = 2x. Äänetty, tämä funktio lukee f x: n olevan 2x. Tämä toiminto on mielenkiintoinen vain, kun käytät sitä tiettyyn x-arvoon. Jos haluat käyttää funktiota muissa yhtälöissä, kirjoita f (2), jolla on sama merkitys kuin 4.

Toisin sanoen, f (2) = 4. Voit ajatella matematiikkafunktiota kartoituksena tuloista lähtöihin. f (x) on tässä tapauksessa x: n tuloarvojen kartoitus vastaaville lähtöarvoille, jotka ovat yhtä suuret tuloarvon ja 2: n tulon kanssa.

JavaScript-toiminnossa funktiolausekkeen arvo on itse toiminto:

kaksinkertainen; // [Toiminto: kaksinkertainen]

Voit nähdä funktion määritelmän .toString () -menetelmällä:

double.toString (); // 'x => x * 2'

Jos haluat käyttää funktiota joihinkin argumentteihin, sinun on käynnistettävä se funktion kutsulla. Funktion kutsu kutsuu funktion argumentteihinsa ja arvioi palautusarvon.

Voit käynnistää toiminnon käyttämällä (argumentti1, argumentti2, ... loput). Esimerkiksi kaksoistoiminnon käynnistämiseksi lisää vain sulkut ja anna arvo kaksinkertaiseksi:

kaksinkertainen (2); // 4

Toisin kuin eräät funktionaaliset kielet, nämä suluissa ovat merkityksellisiä. Ilman niitä toimintoa ei kutsuta:

kaksinkertainen 4; // SyntaxError: Odottamaton numero

allekirjoitukset

Toiminnoilla on allekirjoituksia, jotka koostuvat:

  1. Valinnainen toiminnon nimi.
  2. Luettelo sulkeissa olevista parametrityypeistä. Parametrit voidaan valinnaisesti nimetä.
  3. Palautusarvon tyyppi.

Tyyppisiä allekirjoituksia ei tarvitse määritellä JavaScriptillä. JavaScript-moottori selvittää tyypit ajon aikana. Jos toimitat riittävästi johtolankoja, allekirjoituksen voidaan päätellä myös kehittäjätyökaluilla, kuten IDE (integroitu kehitysympäristö) ja Tern.js, käyttämällä datavirta-analyysiä.

JavaScriptillä ei ole omaa funktion allekirjoitusmerkintää, joten on olemassa joitain kilpailevia standardeja: JSDoc on ollut historiallisesti erittäin suosittu, mutta se on hankalasti sanallinen, ja kukaan ei vaivaudu pitämään doc-kommentteja ajan tasalla koodin kanssa, joten monilla JS-kehittäjillä on lopetti sen käytön.

TypeScript ja Flow ovat tällä hetkellä suuret kilpailijat. En ole varma kuinka ilmaista kaikki tarvittava kummassakin näistä, joten käytän Rtypeä vain dokumentointitarkoituksiin. Jotkut ihmiset palaavat takaisin Haskellin vain curry-Hindley – Milner-tyyppisiin curry-tyyppeihin. Haluaisin nähdä hyvän JavaScriptille standardisoidun merkintäjärjestelmän, jos vain dokumentaatiota varten, mutta en usko, että mikään nykyisistä ratkaisuista on tällä hetkellä tehtävän mukainen. Toistaiseksi siristä ja tee parhaasi pysyäksesi oudon tyyppisissä allekirjoituksissa, jotka todennäköisesti näyttävät hiukan erilaisilta kuin mitä käytät.

functionName (param1: Type, param2: Type) => Type

Tuplan allekirjoitus on:

kaksinkertainen (x: n) => numero

Huolimatta siitä, että JavaScript ei vaadi allekirjoitusten selittämistä, tieto siitä, mitkä allekirjoitukset ovat ja mitä ne tarkoittavat, on silti tärkeää, jotta voimme kommunikoida tehokkaasti toimintojen käytöstä ja niiden muodostamisesta. Useimmat uudelleenkäytettävät toimintojen koostumuksen apuohjelmat vaativat sinun toimittaa toimintoja, joilla on samantyyppinen allekirjoitus.

Parametrien oletusarvot

JavaScript tukee parametrien oletusarvoja. Seuraava funktio toimii kuin identiteettitoiminto (funktio, joka palauttaa saman arvon, jonka syötät), ellet soita sitä määrittelemättömällä tavalla tai yksinkertaisesti lähetä mitään argumentteja - sitten se palauttaa nollan sen sijaan:

const taiZero = (n = 0) => n;

Jos haluat asettaa oletuksen, määritä se parametrille funktion allekirjoituksessa = operaattori, kuten yllä olevassa n = 0. Kun määrität oletusarvot tällä tavalla, kirjoita päätelmätyökalut, kuten Tern.js, Flow tai TypeScript, voivat päätellä toiminnosi tyyppisignaalin automaattisesti, vaikka et nimenomaisesti ilmoittaisi tyyppihuomautuksia.

Seurauksena on, että kun oikeat laajennukset on asennettu editoriin tai IDE: hen, voit nähdä toimintoelementit näkyvän sisäisessä muodossa, kun kirjoitat toimintopuheluita. Voit myös ymmärtää, kuinka toimintoa voidaan käyttää yhdellä silmäyksellä sen puhelun allekirjoituksen perusteella. Oletustehtävien käyttäminen aina, kun se on järkevää, voi auttaa sinua kirjoittamaan enemmän itse dokumentoivia koodeja.

Huomaa: Parametrit, joilla on oletusasetukset, eivät laske toiminnon pituusominaisuutta, koska ne poistavat apuohjelmat, kuten automaattisen leikkauksen, jotka riippuvat .pituusarvosta. Jotkut curry-apuohjelmat (kuten lodash / curry) antavat sinun siirtää mukautetun ariteetin kiertääksesi tätä rajoitusta, jos törmäät siihen.

Nimeltään argumentit

JavaScript-toiminnot voivat ottaa objektikirjaimellisia argumentteina ja käyttää hajottavaa määritystä parametrien allekirjoituksessa saavuttaakseen nimeltään argumentteille vastaavuuden. Huomaa, että voit myös määrittää oletusarvot parametreille oletusparametritoiminnolla:

const createUser = ({
  nimi = 'tuntematon',
  avatarThumbnail = '/avatars/anonymous.png'
}) => ({
  nimi,
  avatarThumbnail
});
const george = createUser ({
  nimi: 'George',
  avatarThumbnail: 'avatarit / shadow-emoji.png'
});
George;
/ *
{
  nimi: 'George',
  avatarThumbnail: 'avatarit / shadow-emoji.png'
}
* /

Lepo ja leviä

JavaScriptin toimintojen yhteinen piirre on kyky koota ryhmä funktion allekirjoitukseen jääviä argumentteja käyttämällä loput operaattoria: ...

Esimerkiksi seuraava funktio yksinkertaisesti hylkää ensimmäisen argumentin ja palauttaa loput taulukkona:

const aTail = (pää, ... häntä) => häntä;
aTail (1, 2, 3); // [2, 3]

Rest kokoaa yksittäiset elementit yhteen taulukkoon. Levitys tapahtuu päinvastoin: se levittää elementit ryhmästä yksittäisiin elementteihin. Harkitse tätä:

const shiftToLast = (pää, ... häntä) => [... häntä, pää];
shiftToLast (1, 2, 3); // [2, 3, 1]

JavaScript-ryhmissä on iteraattori, joka käynnistetään, kun hajotusoperaattoria käytetään. Jokaiselle taulukon kohteelle iteraattori antaa arvon. Lausekkeessa [... häntä, pää] iteraattori kopioi jokaisen elementin peräkkäin peräkkäisestä taulukosta uuteen taulukkoon, jonka ympäröivä kirjaimellinen merkintä on luonut. Koska pää on jo yksittäinen elementti, me vain napsautamme sen taulukon loppuun ja olemme valmiit.

currying

Curryttu toiminto on toiminto, joka ottaa useita parametreja kerrallaan: Se ottaa parametrin ja palauttaa funktion, joka ottaa seuraavan parametrin, ja niin edelleen, kunnes kaikki parametrit on toimitettu, jolloin sovellus on valmis ja lopullinen arvo palautetaan.

Curry ja osittainen sovellus voidaan ottaa käyttöön palauttamalla toinen toiminto:

const ylitys = cutoff => n => n> = cutoff;
const gt4 = ylipäästö (4); // highpass () palauttaa uuden toiminnon

Sinun ei tarvitse käyttää nuolitoimintoja. JavaScriptillä on myös toiminto-avainsana. Käytämme nuolitoimintoja, koska funktion avainsana on paljon tyypillisempi. Tämä vastaa edellä mainittua highPass () -määritelmää:

const highpass = funktion ylitys (raja) {
  paluutoiminto (n) {
    paluu n> = raja;
  };
};

JavaScript-nuoli tarkoittaa karkeasti ”toimintoa”. Funktion käyttäytymisessä on joitain tärkeitä eroja käyttämästäsi toiminnasta riippuen (=> puuttuu oma tämä, eikä sitä voida käyttää rakentajana), mutta pääsemme niihin eroihin saavuttaessamme. Toistaiseksi, kun näet x => x, ajattele "funktiota, joka ottaa x: n ja palauttaa x". Joten voit lukea const highpass = cutoff => n => n> = cutoff; kuten:

”Ylitys on funktio, joka ottaa raja-arvon ja palauttaa funktion, joka ottaa n: n ja palauttaa tuloksen n> = raja-arvo”.

Koska highpass () palauttaa funktion, voit käyttää sitä luomaan erikoistuneemman toiminnon:

const gt4 = ylipäästö (4);
GT4 (6); // totta
GT4 (3); // väärä

Autocurry antaa sinulle curry-toiminnot automaattisesti, maksimaalisen joustavuuden. Oletetaan, että sinulla on funktio add3 ():

const add3 = curry ((a, b, c) => a + b + c);

Automaattisen leikkauksen avulla voit käyttää sitä useilla eri tavoilla, ja se palauttaa oikean asian riippuen kuinka monta argumenttia syötät:

add3 (1, 2, 3); // 6
add3 (1,2) (3); // 6
add3 (1) (2,3); // 6
ADD3 (1) (2) (3); // 6

Valitettavasti Haskell-faneja, JavaScript ei sisällä sisäänrakennettua automaattisen korotuksen mekanismia, mutta voit tuoda sellaisen Lodashista:

$ npm install - säästää lodash

Sitten moduuleissasi:

tuoda currya 'lodashista / currysta';

Tai voit käyttää seuraavaa taikuusloitsua:

// Pieni, rekursiivinen omatoimisuus
const curry = (
  f, arr = []
) => (... args) => (
  a => a.length === f.length?
    f (... a):
    curry (f, a)
) ([... arr, ... args]);

Toiminnan koostumus

Tietenkin voit säveltää toimintoja. Funktion koostumus on prosessi, jossa yhden funktion palautusarvo välitetään argumentiksi toiselle funktiolle. Matemaattisessa merkinnässä:

f. g

Mikä kääntää tämän JavaScript:

f (g (x))

Sitä arvioidaan sisältäpäin:

  1. x arvioidaan
  2. g () lisätään x: lle
  3. f () käytetään palautusarvoon g (x)

Esimerkiksi:

const inc = n => n + 1;
inc (kaksinkertainen (2)); // 5

Arvo 2 välitetään kaksinkertaiseksi (), mikä tuottaa 4. 4 siirretään inc (): ksi, joka arvioidaan arvoksi 5.

Voit siirtää minkä tahansa lausekkeen argumenttina funktiolle. Lauseke arvioidaan ennen funktion soveltamista:

lisää (kaksinkertainen (2) * kaksinkertainen (2)); // 17

Koska kaksinkertainen (2) arvioi 4: ksi, voit lukea sen inc: nä (4 * 4), joka arvioi inc: ksi (16), joka sitten arvioi 17: ksi.

Toimintojen koostumus on keskeinen toiminnallisessa ohjelmoinnissa. Meillä on paljon enemmän asiaa myöhemmin.

taulukot

Matriiseissa on joitain sisäänrakennettuja menetelmiä. Menetelmä on objektiin liittyvä toiminto: yleensä siihen liittyvän objektin ominaisuus:

const arr = [1, 2, 3];
arr.map (double); // [2, 4, 6]

Tässä tapauksessa arr on objekti, .map () on objektin ominaisuus, jolla on arvon funktio. Kun kutsut sen, funktio saatetaan argumentteihin samoin kuin erityinen parametri, jota kutsutaan tähän, joka asetetaan automaattisesti, kun menetelmää kutsutaan. Tämä arvo on, kuinka .map () pääsee käsiksi taulukon sisältöön.

Huomaa, että välitämme kaksinkertaisen funktion arvona karttaan sen sijaan, että kutsumme sitä. Tämä johtuu siitä, että kartta vie funktion argumenttina ja soveltaa sitä jokaisessa taulukon kohteessa. Se palauttaa uuden taulukon, joka sisältää arvot, jotka palautetaan kaksinkertaisella ().

Huomaa, että alkuperäinen arr-arvo ei ole muuttunut:

sov; // [1, 2, 3]

Menetelmän ketjuttaminen

Voit myös ketjuttaa menetelmäpuhelut. Menetelmäketju on prosessi, jossa menetelmää kutsutaan suoraan funktion palautusarvoon tarvitsematta viitata paluuarvoon nimeltä:

const arr = [1, 2, 3];
arr.map (double) .map (double); // [4, 8, 12]

Predikaatti on funktio, joka palauttaa boolen arvon (tosi tai väärä). .Filter () -menetelmä ottaa predikaatin ja palauttaa uuden luettelon valitsemalla uudessa luettelossa vain kohteet, jotka läpäisevät predikaatin (palata true):

[2, 4, 6] .suodatin (gt4); // [4, 6]

Usein haluat valita kohteita luettelosta ja yhdistää ne sitten uuteen luetteloon:

[2, 4, 6] .suodatin (gt4) .map (kaksinkertainen); [8, 12]

Huomaa: Myöhemmin tässä tekstissä näet tehokkaamman tavan valita ja karttaa samanaikaisesti käyttämällä jotain, jota kutsutaan anturiksi, mutta on myös muita tutkittavaa asioita.

johtopäätös

Jos pääsi pyörii juuri nyt, älä huoli. Me tuskin raaputimme pintaan monia asioita, jotka ansaitsevat paljon enemmän tutkimusta ja huomiointia. Kiertymme takaisin ja tutkimme joitain näistä aiheista paljon perusteellisemmin pian.

Osta kirja | Hakemisto |

Lisätietoja EricElliottJS.com

EricElliottJS.com-sivuston jäsenille on tarjolla videotunteja interaktiivisilla koodihaasteilla. Jos et ole jäsen, kirjaudu tänään.

Eric Elliott on hajautettu järjestelmäasiantuntija ja kirjojen kirjoittaminen “Composing Software” ja “Programming JavaScript Applications”. DevAnywhere.io: n perustajana hän opettaa kehittäjille taitoja, joita he tarvitsevat etätyöskentelyyn ja työ- ja yksityiselämän tasapainon omaksumiseen. Hän rakentaa ja neuvoo kehitystyöryhmiä salaustekniikkaprojekteihin ja on osallistunut ohjelmistokokemuksiin Adobe Systemsille, Zumba Fitnessille, The Wall Street Journalille, ESPN: lle, BBC: lle ja huipputalostajille, mukaan lukien Usher, Frank Ocean, Metallica ja monille muille.

Hän nauttii kauko-elämäntavasta maailman kauneimman naisen kanssa.