Pažinčių šališkumas jus sulaiko: atėjo laikas įsitraukti į rodyklių funkcijas

„Inkaras“ - Aktorius212 - (CC BY-NC-ND 2.0)

Aš moku „JavaScript“ pragyvenimui. Pastaruoju metu keisčiau savo mokymo programą, kad greičiau - per pirmąsias kelias pamokas - išmokčiau kreivų strėlių funkcijų. Perkėliau jį anksčiau į mokymo programą, nes tai yra labai vertingas įgūdis, o studentai pasirenka kreivas rodykles daug greičiau, nei maniau.

Jei jie gali tai suprasti ir tuo anksčiau pasinaudoti, kodėl gi to nemokyti anksčiau?

Pastaba: Mano kursai nėra skirti žmonėms, kurie anksčiau niekada nebuvo palietę kodo eilutės. Daugelis studentų prisijungia praleidę bent kelis mėnesius kodavimo - savarankiškai, įkrovos stovykloje ar profesionaliai. Tačiau mačiau daugybę jaunesnių kūrėjų, turinčių mažai patirties arba visai neturinčių patirties, šiomis temomis pasirinkti greitai.

Mačiau, kaip per vieną valandos pamoką krūva studentų susipažįsta su kreivomis rodyklių funkcijomis. (Jei esate „Sužinokite„ JavaScript “su Ericu Elliottu“ narys, dabar galite žiūrėti 55 minučių ES6 „Curry & Composition“ pamoką).

Matydamas, kaip greitai studentai jį pasiima ir pradeda naudotis savo naujai atrastomis kario galiomis, aš visada šiek tiek nustebau, kai „Twitter“ paskelbiu kreivų rodyklių funkcijas, o „Twitterverse“ reaguoja pasipiktinusi mintimi padaryti tą „neįskaitomą“ kodą žmonių, kuriems tai reikės išlaikyti.

Pirmiausia leiskite pateikti pavyzdį, apie ką mes kalbame. Pirmą kartą pastebėjęs neigiamą įspūdį buvo „Twitter“ atsakymas į šią funkciją:

const secret = msg => () => msg;

Buvau šokiruota, kai žmonės „Twitter“ apkaltino mane bandymu supainioti žmones. Aš parašiau šią funkciją, kad parodyčiau, kaip lengva išreikšti kreivas funkcijas ES6. Tai yra paprasčiausias praktinis uždarymo būdas ir jo išraiška, apie kurį galiu galvoti „JavaScript“. (Susijęs: „Kas yra uždarymas?“).

Tai atitinka šią funkcijos išraišką:

const secret = function (msg) {
  grąžinimo funkcija () {
    grąžinti msg;
  };
};

Secret () yra funkcija, kuri priima pranešimą ir grąžina naują funkciją, kuri grąžina pranešimą. Norėdami nustatyti msg reikšmę bet kuriai vertei, kurią perduosite slaptoje (), pasinaudojama uždarymo galimybėmis.

Štai kaip jūs juo naudojatės:

const mySecret = slaptas ('labas');
Mano paslaptis(); // „labas“

Pasirodo, „dviguba strėlė“ supainiojo žmones. Esu įsitikinęs, kad tai faktas:

Žinodami, rodyklės funkcijos yra skaitomiausias būdas išreikšti kreivas funkcijas „JavaScript“.

Daugelis žmonių man teigė, kad ilgesnę formą lengviau perskaityti nei trumpesnę. Jie iš dalies teisūs, bet dažniausiai neteisūs. Tai aiškiau ir aiškiau, bet ne lengviau perskaityti - bent jau ne tam, kas pažįsta rodyklių funkcijas.

Prieštaravimai, kuriuos mačiau „Twitter“, nebuvo susiję su sklandžia mokymosi patirtimi, kuria mėgavosi mano studentai. Remdamasis mano patirtimi, studentai naudojasi kreivomis rodyklėmis, tokiomis kaip žuvys. Per kelias dienas jų išmokę, jie yra vienas su strėlėmis. Jie pastumia juos, norėdami išspręsti įvairius kodavimo iššūkius.

Nematau jokio ženklo, kad rodyklių funkcijas jiems „sunku“ išmokti, skaityti ar suprasti - kai tik jie pradės investiciją į jų mokymąsi per kelias 1 valandos pamokas ir studijų sesijas.

Jie lengvai skaito kreivas rodyklių funkcijas, kurių dar niekada nebuvo matę, ir man paaiškina, kas vyksta. Jie natūraliai rašo patys, kai pateikiu jiems iššūkį.

Kitaip tariant, kai tik jie susipažįsta pamatę kreivas rodyklės funkcijas, jie su jais neturi problemų. Jie skaito juos taip lengvai, kaip jūs skaitote šį sakinį - ir jų supratimas atsispindi daug paprastesniame kode su mažiau klaidų.

Kodėl kai kurie žmonės mano, kad senosios funkcijos išraiškos atrodo „lengviau“ skaitomos

Pažintinis šališkumas yra išmatuojamas žmogaus pažintinis šališkumas, verčiantis mus priimti destruktyvius sprendimus, nepaisant žinojimo apie geresnį variantą. Mes ir toliau naudojame tuos pačius senus modelius, nepaisant to, kad žinome apie geresnius modelius iš patogumo ir įpročio.

Galite sužinoti daugiau apie pažinimo šališkumą (ir daugybę kitų būdų, kaip save apgaudinėti) iš puikios knygos „Atšaukiantis projektas: draugystė, kuri pakeitė mūsų mintis“. Reikėtų perskaityti šią knygą kiekvienam programinės įrangos kūrėjui, nes ji skatina kritiškiau mąstyti ir tikrinti savo prielaidas, kad nepatektų į įvairius pažinimo spąstus - ir pasakojimas apie tai, kaip tie pažinimo spąstai buvo aptikti, taip pat yra tikrai geras. .

Senosios funkcijos išraiškos tikriausiai sukelia klaidas jūsų kode

Šiandien perrašiau kreivos strėlės funkciją iš ES6 į ES5, kad galėčiau paskelbti ją kaip atvirojo kodo modulį, kurį žmonės galėtų naudoti senose naršyklėse neperprašydami. ES5 versija mane šokiravo.

ES6 versija buvo paprasta, trumpa ir elegantiška - tik 4 eilutės.

Aš tikrai pagalvojau, kad ši funkcija „Twitter“ įrodo, kad rodyklės funkcijos yra pranašesnės ir kad žmonės turėtų atsisakyti savo senųjų funkcijų, pavyzdžiui, blogo įpročio.

Taigi aš tweet:

Čia pateiktas funkcijų tekstas, jei vaizdas neveikia jūsų:

// kreivai rodyklėmis
const composeMixins = (... mixins) => (
  egzempliorius = {},
  mix = (... fns) => x => fns.reduce ((acc, fn) => fn (acc), x)
) => maišyti (... mixins) (instancija);
// vs ES5 stilius
var composeMixins = function () {
  var mixins = [] .slice.call (argumentai);
  grąžinimo funkcija (egzempliorius, mišinys) {
    if (! instancija) instancija = {};
    if (! maišyti) {
      maišyti = funkcija () {
        var fns = [] .slice.call (argumentai);
        grąžinimo funkcija (x) {
          grąžinti fns.reduce (funkcija (acc, fn) {
            grįžti fn (acc);
          }, x);
        };
      };
    }
    grįžti mix.apply (nulis, mixins) (instancija);
  };
};

Aptariama funkcija yra paprastas įvyniojimas aplink vamzdį (), standartinis funkcinio programavimo įrankis, paprastai naudojamas funkcijoms sudaryti. Vamzdžio () funkcija egzistuoja „lodash“ kaip „liedash“ / „flow“, „Ramda“ kaip „R.pipe“ () ir netgi turi savo operatorių keliomis funkcinėmis programavimo kalbomis.

Tai turėtų būti pažįstamas visiems, žinantiems apie funkcinį programavimą. Kaip ir jo pirminė priklausomybė: Sumažinkite.

Šiuo atveju jis naudojamas komponuoti funkcinius derinius, tačiau tai nėra svarbi detalė (ir visas kitas tinklaraščio įrašas). Čia yra svarbi informacija:

Ši funkcija užima daugybę funkcinių mišinių ir grąžina funkciją, kuri juos vieną po kito taiko dujotiekyje - kaip surinkimo linija. Kiekvienas funkcinis maišytuvas pasirenka pavyzdį kaip įvestį ir priklijuoja tam tikrus daiktus prieš perduodamas kitai dujotiekio funkcijai.

Jei praleisite egzempliorių, jums bus sukurtas naujas objektas.

Kartais mes galime norėti, kad komiksai būtų komponuojami skirtingai. Pvz., Jei norite pakeisti pirmenybės tvarką, galbūt norėsite perduoti kompozitorių (), o ne vamzdį ().

Jei jums nereikia tinkinti elgsenos, paprasčiausiai palikite numatytąją nuostatą ir gaukite standartinę elgseną.

Tiesiog faktai

Pateikiamos nuomonės apie skaitomumą, išskyrus objektyvius faktus, susijusius su šiuo pavyzdžiu:

  • Turiu ilgametę patirtį tiek su ES5, tiek su ES6 funkcijų išraiškomis, rodyklėmis ar kitu būdu. Šių duomenų pažinimo šališkumas nėra kintamasis.
  • Parašiau ES6 versiją per kelias sekundes. Jame nebuvo jokių klaidų (apie kurias aš žinau - ji praeina visus savo vieneto testus).
  • Parašyti ES5 versiją man prireikė kelių minučių. Bent jau eilės tvarka daugiau laiko. Minutės vs sekundės. Du kartus praradau vietą funkcijų įtraukose. Parašiau 3 klaidas, kurias visas turėjau derinti ir taisyti. Dvi iš jų aš turėjau kreiptis į console.log () norėdamas išsiaiškinti, kas vyksta.
  • ES6 versija yra 4 kodo eilutės.
  • ES5 versija yra 21 eilutės ilgio (17 iš tikrųjų turi kodą).
  • Nepaisant varginančio aiškumo, ES5 versija iš tikrųjų praranda dalį informacijos, kurią galima rasti ES6 versijoje, ištikimybės. Tai daug ilgiau, bet mažiau bendraujama, skaitykite toliau.
  • ES6 versijoje yra 2 funkcijų parametrų skaičiuoklės. ES5 versijoje praleidžiami skaičiai, o vietoj to naudojamas objektas „netiesioginiai argumentai“, o tai kenkia funkcijos parašo skaitomumui (patikimumo laipsnio sumažėjimas 1).
  • ES6 versija apibrėžia numatytąjį mišinio funkcijų paraše, kad galėtumėte aiškiai pamatyti, kad tai yra parametro vertė. ES5 versija užtemdo šią detalę, o paslepia ją giliai funkcijos korpuse. (ištikimybės laipsnis 2).
  • ES6 versijoje yra tik 2 įtraukų lygiai, kurie padeda paaiškinti, kaip ją reikia perskaityti. „ES5“ versija turi 6, o įterpimo lygiai neaiškūs, o ne palengvina funkcijos struktūros skaitomumą (ištikimybės laipsnis yra 3).

ES5 versijoje vamzdis () užima didžiąją dalį funkcijų korpuso - tiek, kad jį apibrėžti įdėta yra šiek tiek beprotiška. Kad ES5 versiją būtų galima perskaityti, ją tikrai reikia išskaidyti į atskirą funkciją:

var pipe = function () {
  var fns = [] .slice.call (argumentai);
  grąžinimo funkcija (x) {
    grąžinti fns.reduce (funkcija (acc, fn) {
      grįžti fn (acc);
    }, x);
  };
};
var composeMixins = function () {
  var mixins = [] .slice.call (argumentai);
  grąžinimo funkcija (egzempliorius, mišinys) {
    if (! instancija) instancija = {};
    if (! mix) mix = pipe;
    grįžti mix.apply (nulis, mixins) (instancija);
  };
};

Man tai atrodo aiškiau skaitoma ir suprantama.

Pažiūrėkime, kas nutinka, kai pritaikome tą patį skaitomumo „optimizavimą“ ES6 versijai:

const pipe = (... fns) => x => fns.reduce ((acc, fn) => fn (acc), x);
const composeMixins = (... mixins) => (
  egzempliorius = {},
  maišyti = vamzdis
) => maišyti (... mixins) (instancija);

Kaip ir ES5 optimizavimas, ši versija yra daugžodingesnė (ji prideda naują kintamąjį, kurio anksčiau nebuvo). Skirtingai nuo ES5 versijos, ši versija nėra žymiai suprantamesnė, apibendrinus vamzdžio apibrėžimą. Galų gale, jis jau turėjo kintamąjį pavadinimą, aiškiai priskirtą funkcijos parašu: maišyti.

Mišinio apibrėžimas jau buvo pateiktas savo eilutėje, todėl skaitytojams nėra sunku susipainioti dėl to, kur jis baigiasi, o likusi funkcija tęsiama.

Dabar turime 2 kintamuosius, vaizduojančius tą patį, o ne 1. Ar mes labai daug pasiekėme? Akivaizdu, kad ne.

Taigi kodėl ES5 versija yra akivaizdžiai geresnė, jei yra paimta ta pati funkcija?

Nes ES5 versija yra akivaizdžiai sudėtingesnė. Šio sudėtingumo priežastis yra šio klausimo esmė. Aš tvirtinu, kad sudėtingumo šaltinis yra sintaksės triukšmas, o sintaksės triukšmas užgožia funkcijos prasmę, o ne padeda.

Perjunkime pavaras ir pašalinsime dar keletą kintamųjų. Panaudokime ES6 abiem pavyzdžiais ir palyginkime tik rodyklės funkcijas su senųjų funkcijų išraiškomis:

var composeMixins = funkcija (... mixins) {
  grąžinimo funkcija (
    egzempliorius = {},
    mix = function (... fns) {
      grąžinimo funkcija (x) {
        grąžinti fns.reduce (funkcija (acc, fn) {
          grįžti fn (acc);
        }, x);
      };
    }
  ) {
    grįžtamasis mišinys (... mixins) (instancija);
  };
};

Man tai atrodo žymiai skaitomiau. Viskas, ką mes pakeitėme, yra tai, kad pasinaudojame poilsio ir numatytųjų parametrų sintaksėmis. Žinoma, kad šią versiją būtų lengviau perskaityti, jūs turite būti susipažinęs su poilsio ir numatytosiomis sintaksėmis, tačiau, net jei jos neturite, manau, akivaizdu, kad ši versija vis tiek yra mažiau užgriozdinta.

Tai labai padėjo, bet man vis tiek aišku, kad ši versija vis dar yra pakankamai užgriozdinta, kad akivaizdinis vamzdžio () paėmimas į savo funkciją akivaizdžiai padėtų:

const pipe = function (... fns) {
  grąžinimo funkcija (x) {
    grąžinti fns.reduce (funkcija (acc, fn) {
      grįžti fn (acc);
    }, x);
  };
};
// Senosios funkcijos išraiškos
const composeMixins = funkcija (... mišiniai) {
  grąžinimo funkcija (
    egzempliorius = {},
    maišyti = vamzdis
  ) {
    grįžtamasis mišinys (... mixins) (instancija);
  };
};

Tai geriau, tiesa? Dabar, kai mišinio priskyrimas užima tik vieną eilutę, funkcijos struktūra yra daug aiškesnė, tačiau mano skoniui vis dar yra per daug sintaksės triukšmo. „ComposMixins“ () man iš pirmo žvilgsnio nėra aišku, kur baigiasi viena funkcija, o prasideda kita.

Atrodo, kad užuot iškvietęs funkcijų kūnus, šis funkcijos raktinis žodis vizualiai susimaišo su aplink jį esančiais identifikatoriais. Mano funkcijoje slepiasi funkcijos! Kur baigiasi parametro parašas ir prasideda funkcijos kūnas? Aš galiu tai išsiaiškinti, jei atidžiai pažiūriu, bet man tai nėra vizualiai akivaizdu.

Kas nutiktų, jei galėtume atsikratyti funkcijos raktinio žodžio ir iškviesti grįžtamąsias vertes vizualiai nukreipdami į jas dideliu riebumu rodykle =>, užuot parašę grįžtamąjį raktinį žodį, susiliejantį su aplinkiniais identifikatoriais?

Pasirodo, mes galime, ir štai kas atrodo:

const composeMixins = (... mixins) => (
  egzempliorius = {},
  maišyti = vamzdis
) => maišyti (... mixins) (instancija);

Dabar turėtų būti aišku, kas vyksta. „composeMixins“ () yra funkcija, kuri priima bet kokį skaičių derinimų ir grąžina funkciją, kuri paima du pasirenkamus parametrus, pvz., ir mišinį. Tai grąžina vamzdynų egzemplioriaus rezultatą per sudarytus mišinius.

Dar vienas dalykas ... jei vamzdyje () pritaikome tą patį optimizavimą, jis stebuklingai virsta įdėklu:

const pipe = (... fns) => x => fns.reduce ((acc, fn) => fn (acc), x);

Turint tokį apibrėžimą vienoje eilutėje, nėra aiškus pranašumas, jei jį apibendrinsite pagal savo funkciją. Atminkite, kad ši funkcija egzistuoja kaip naudinga priemonė „Lodash“, „Ramda“ ir daugybė kitų bibliotekų, tačiau ar tikrai verta importuoti kitą biblioteką?

Ar net verta traukti jį į savo liniją? Tikriausiai. Tai iš tikrųjų yra dvi skirtingos funkcijos, ir jas atskyrus, tai tampa aiškiau.

Kita vertus, turėdami jį tiesioginį, paaiškinsite tipo ir naudojimo lūkesčius, kai žiūrėsite į parametro parašą. Štai, kas nutinka, kai ją sukuriame tiesiogiai:

const composeMixins = (... mixins) => (
  egzempliorius = {},
  mix = (... fns) => x => fns.reduce ((acc, fn) => fn (acc), x)
) => maišyti (... mixins) (instancija);

Dabar grįžtame prie pradinės funkcijos. Pakeliui neišmesdavome jokios prasmės. Tiesą sakant, deklaruodami savo parametrus ir numatytąsias vertes netiesiogiai, mes pridėjome informacijos apie tai, kaip naudojama funkcija, ir kaip galėtų atrodyti parametrų vertės.

Visas tas papildomas kodas ES5 versijoje buvo tik triukšmas. Sintaksės triukšmas. Tai nebuvo naudinga, išskyrus aklimatizuoti žmones, kurie nepažįsta kreivų strėlių funkcijų.

Įgijus pakankamą susipažinimą su kreivomis rodyklių funkcijomis, turėtų būti aišku, kad pirminė versija yra lengviau skaitoma, nes pasiklysti yra daug mažiau sintaksės.

Tai taip pat yra mažiau linkusi į klaidas, nes klaidų paslėpimo plotas yra daug mažesnis.

Įtariu, kad pasenusiose funkcijose slepiasi daugybė klaidų, kurios būtų rastos ir pašalintos, jei atnaujintumėte į rodyklių funkcijas.

Taip pat tikiuosi, kad jūsų komanda taps žymiai produktyvesnė, jei išmoksite priimti ir palaikyti daugiau glaustos sintaksės, esančios ES6.

Tiesa, kad kai kuriuos dalykus lengviau suprasti, jei jie aiškinami, tačiau taip pat tiesa, kad paprastai mažiau kodo, tuo geriau.

Jei mažiau kodo gali atlikti tą patį dalyką ir daugiau bendrauti, neprarandant jokios prasmės, tai yra objektyviai geriau.

Svarbiausia žinoti skirtumą yra prasmė. Jei daugiau kodo nepateikia daugiau prasmės, to kodo neturėtų būti. Ši sąvoka yra tokia pagrindinė, ji yra gerai žinoma natūralios kalbos stiliaus gairė.

Tos pačios stiliaus gairės galioja ir šaltinio kodams. Apkabink jį, ir tavo kodas bus geresnis.

Dienos pabaigoje šviesa tamsoje. Atsakydamas į dar vieną tviterį, kuriame sakoma, kad ES6 versija yra mažiau skaitoma:

Laikas susipažinti su ES6, jo kūrimu ir funkcijų sudarymu.

Tolesni žingsniai

„Sužinokite„ JavaScript “su Ericu Elliottu“ nariai dabar gali žiūrėti 55 minučių ES6 „Curry & Composition“ pamoką.

Jei nesate narys, praleidžiate!

Erikas Elliotas yra „Java“ programų programavimo („O'Reilly“) ir „Sužinokite„ JavaScript “naudodamas Ericą Elliottą“ autorius. Jis prisidėjo prie programinės įrangos patirties „Adobe Systems“, „Zumba Fitness“, „The Wall Street Journal“, ESPN, BBC ir geriausių įrašų atlikėjų, įskaitant Usherį, Franką Okeaną, „Metallica“ ir daugelį kitų.

Didžiąją laiko dalį jis praleidžia San Fransisko įlankos rajone su gražiausia moterimi pasaulyje.