Avage JavaScripti toruoperaatori võimsus elegantse, loetava ja tõhusa koodi saavutamiseks osalise funktsioonirakenduse kaudu. Globaalne juhend tänapäevastele arendajatele.
JavaScripti toruoperaatori meisterdamine osalise funktsioonirakenduse abil
JavaScripti arenduse pidevalt arenevas maastikus ilmuvad uued funktsioonid ja mustrid, mis võivad oluliselt parandada koodi loetavust, hooldatavust ja tõhusust. Üks selline võimas kombinatsioon on JavaScripti toruoperaator, eriti kui seda kasutatakse koos osaliselt funktsioonirakendusega. Selle ajaveebi eesmärk on neid mõisteid selgitada, pakkudes põhjalikku juhendit arendajatele kogu maailmas, olenemata nende varasemast kokkupuutest funktsionaalse programmeerimise paradigmaga.
JavaScripti toruoperaatori mõistmine
Toruoperaator, mida sageli tähistab toru sümbol | või mõnikord |>, on kavandatav ECMAScripti funktsioon, mis on loodud funktsioonide jada rakendamise protsessi lihtsustamiseks väärtusele. Traditsiooniliselt võib funktsioonide aheldamine JavaScriptis mõnikord viia sügavalt pesastatud väljakutseteni või vajada vahepealseid muutujaid, mis võivad andmevoo kavatsuse varjata.
Probleem: Pikad funktsioonide ahelad
Arvestage stsenaariumiga, kus peate andmetükil tegema rea ​​muudatusi. Ilma toruoperaatorita võiksite kirjutada midagi sellist:
const processData = (data) => {
const step1 = addPrefix(data, 'processed_');
const step2 = toUpperCase(step1);
const step3 = addSuffix(step2, '_final');
return step3;
};
// Või aheldamist kasutades:
const processDataChained = (data) => addSuffix(toUpperCase(addPrefix(data, 'processed_')), '_final');
Kuigi aheldatud versioon on lühem, loetakse seda seestpoolt väljapoole. addPrefix funktsiooni rakendatakse esmalt, seejärel selle tulemus antakse toUpperCase, ja lõpuks selle tulemuse tulemus antakse addSuffix.
Lahendus: Toruoperaator
Toruoperaator püüab seda probleemi lahendada, võimaldades funktsioone rakendada järjestikku vasakult paremale, muutes andmevoo selgeks ja intuitiivseks. Kui toruoperaator |> oleks natiivne JavaScripti funktsioon, saaks sama toimingut väljendada järgmiselt:
const processDataPiped = (data) => data
|> addPrefix('processed_')
|> toUpperCase
|> addSuffix('_final');
See loeb loomulikult: võtke data, rakendage sellele addPrefix('processed_'), seejärel rakendage selle tulemusele toUpperCase ja lõpuks rakendage selle tulemusele addSuffix('_final'). Andmed voolavad toimingute kaudu selges, lineaarsetes.
Praegune olek ja alternatiivid
On oluline märkida, et toruoperaator on endiselt 1. etapi ECMAScripti ettepanek. Kuigi sellel on suur potentsiaal, pole see veel standardne JavaScripti funktsioon. Kuid see ei tähenda, et te ei saaks selle kontseptuaalsest võimsusest täna kasu saada. Saame selle käitumist simuleerida erinevate tehnikate abil, millest kõige elegantsem hõlmab osalist funktsioonirakendust.
Mis on osaline funktsioonirakendus?
Osaline funktsioonirakendus on funktsionaalse programmeerimise tehnika, kus saate fikseerida mõned funktsiooni argumendid ja luua uue funktsiooni, mis ootab ülejäänud argumente. See erineb karjumisest, kuigi on sellega seotud. Karjumine teisendab mitut argumenti võtva funktsiooni funktsioonide jadaks, millest igaüks võtab ühe argumendi. Osali rakendamine fikseerib argumendid ilma, et funktsiooni tingimata ühe argumendiga funktsioonideks jagaks.
Lihtne näide
Kujutagem ette funktsiooni, mis liidab kaks numbrit:
const add = (a, b) => a + b;
console.log(add(5, 3)); // Väljund: 8
NĂĽĂĽd loome osaliselt rakendatud funktsiooni, mis alati lisab antud numbrile 5:
const addFive = (b) => add(5, b);
console.log(addFive(3)); // Väljund: 8
console.log(addFive(10)); // Väljund: 15
Siin on addFive uus funktsioon, mis on tuletatud add-ist esimese argumendi (a) fikseerimisega väärtusele 5. See vajab nüüd ainult teist argumenti (b).
Kuidas saavutada osaline rakendamine JavaScriptis
JavaScripti sisseehitatud meetodid, nagu bind ja ülejäänud/levitamise süntaks, pakuvad võimalusi osaliseks rakendamiseks.
bind() kasutamine
bind() meetod loob uue funktsiooni, millel, kui seda kutsutakse, on this võtmesõna seatud antud väärtusele, kusjuures antud argumentide jada eelneb kõigile, mis antakse uuele funktsioonile helistamisel.
const multiply = (x, y) => x * y;
// Osali rakendada esimene argument (x) väärtusele 10
const multiplyByTen = multiply.bind(null, 10);
console.log(multiplyByTen(5)); // Väljund: 50
console.log(multiplyByTen(7)); // Väljund: 70
Selles näites loob multiply.bind(null, 10) uue funktsiooni, kus esimene argument (x) on alati 10. null antakse bind esimese argumendina, kuna me ei hooli this kontekstist antud juhul.
Noolt funktsioonid ja ülejäänud/levitamise süntaks
Moodsam ja sageli loetavam lähenemisviis on kasutada nooltega funktsioone koos ülejäänud ja levitamise süntaksiga.
const divide = (numerator, denominator) => numerator / denominator;
// Osali rakendada nimetaja
const divideByTwo = (numerator) => divide(numerator, 2);
console.log(divideByTwo(10)); // Väljund: 5
console.log(divideByTwo(20)); // Väljund: 10
// Osali rakendada lugeja
const divideTwoBy = (denominator) => divide(2, denominator);
console.log(divideTwoBy(4)); // Väljund: 0.5
console.log(divideTwoBy(1)); // Väljund: 2
See lähenemisviis on väga selge ja töötab hästi väikese, fikseeritud arvu argumentidega funktsioonide puhul. Paljude argumentidega funktsioonide puhul võib robustsem abifunktsioon olla kasulik.
Osalise rakendamise eelised
- Koodi taaskasutatavus: looge ĂĽldotstarbeliste funktsioonide spetsialiseeritud versioonid.
- Loetavus: muudab keerulised toimingud lihtsamaks mõistmiseks, jagades need osadeks.
- Moodulaarsus: funktsioonid muutuvad komposiitsemaks ja neid on üksikult lihtsam mõista.
- DRY põhimõte: väldib samade argumentide kordamist mitme funktsiooni kutsel.
Toruoperaatori simuleerimine osalise rakendamisega
Nüüd toome need kaks mõistet kokku. Saame toruoperaatorit simuleerida, luues abifunktsiooni, mis võtab väärtuse ja funktsioonide massiivi, mida sellele järjestikku rakendatakse. Oluline on, et meie funktsioonid peavad olema struktureeritud nii, et nad aktsepteerivad vahepealset tulemust oma esimese argumendina, kus osaline rakendamine särab.
pipe abifunktsioon
Määratleme pipe funktsiooni, mis seda saavutab:
const pipe = (initialValue, fns) => {
return fns.reduce((acc, fn) => fn(acc), initialValue);
};
See pipe funktsioon võtab initialValue ja funktsioonide massiivi (fns). See kasutab reduce-i, et rakendada iga funktsiooni (fn) kumulatiivile (acc), alustades initialValue-ist. Selleks, et see sujuvalt töötaks, peab iga fns funktsioon olema valmis aktsepteerima eelmise funktsiooni väljundit oma esimese argumendina.
Torudeks funktsioonide ettevalmistamine
Siin muutub osaline rakendamine asendamatuks. Kui meie algsed funktsioonid ei aktsepteeri vahepealset tulemust loomulikult oma esimese argumendina, peame neid kohandama. Vaatame meie algset addPrefix näidet:
const addPrefix = (prefix, str) => `${prefix}${str}`;
const toUpperCase = (str) => str.toUpperCase();
const addSuffix = (str, suffix) => `${str}${suffix}`;
Et pipe funktsioon töötaks, vajame funktsioone, mis võtavad esmalt stringi ja seejärel teised argumendid. Saame seda saavutada osalise rakendamisega:
// Argumentide osaline rakendamine, et need sobiksid torujuhtme ootustega
const addProcessedPrefix = (str) => addPrefix('processed_', str);
const addFinalSuffix = (str) => addSuffix(str, '_final');
// NĂĽĂĽd kasutage torujuhtme abifunktsiooni
const data = "hello";
const processedData = pipe(data, [
addProcessedPrefix,
toUpperCase,
addFinalSuffix
]);
console.log(processedData); // Väljund: PROCESSED_HELLO_FINAL
See töötab suurepäraselt. addProcessedPrefix funktsioon luuakse addPrefix prefix argumendi fikseerimisega. Samamoodi fikseerib addFinalSuffix addSuffix suffix argumendi. toUpperCase funktsioon sobib juba malliga, kuna see võtab ainult ühe argumendi (stringi).
Elegantsem pipe funktsioonitehastajatega
Saame oma pipe funktsiooni veelgi paremini kohandada kavandatud toruoperaatori süntaksiga, luues funktsiooni, mis tagastab torujuhtme toimingu ise. See hõlmab mõtteviisi nihet, kus selle asemel, et algväärtust otse pipe-le edastada, edastame selle hiljem.
Loome pipeline funktsiooni, mis võtab funktsioonide jada ja tagastab uue funktsiooni, mis on valmis algväärtuse aktsepteerimiseks:
const pipeline = (...fns) => {
return (initialValue) => {
return fns.reduce((acc, fn) => fn(acc), initialValue);
};
};
// NĂĽĂĽd valmistame funktsioonid ette (nagu varem)
const addPrefix = (prefix, str) => `${prefix}${str}`;
const toUpperCase = (str) => str.toUpperCase();
const addSuffix = (str, suffix) => `${str}${suffix}`;
const addProcessedPrefix = (str) => addPrefix('processed_', str);
const addFinalSuffix = (str) => addSuffix(str, '_final');
// Loome torujuhtme toimingu funktsiooni
const processPipeline = pipeline(
addProcessedPrefix,
toUpperCase,
addFinalSuffix
);
// NĂĽĂĽd rakendage seda andmetele
const data1 = "world";
console.log(processPipeline(data1)); // Väljund: PROCESSED_WORLD_FINAL
const data2 = "javascript";
console.log(processPipeline(data2)); // Väljund: PROCESSED_JAVASCRIPT_FINAL
See pipeline funktsioon loob korduvkasutatava toimingu. Määratleme transformatsioonide jada üks kord ja seejärel saame seda järjestust rakendada mis tahes arvule sisendväärtustele.
bind kasutamine funktsioonide ettevalmistamiseks
Saame kasutada ka bind-i funktsioonide ettevalmistamiseks, mis võib olla eriti kasulik, kui töötate olemasolevate koodibaaside või teekidega, mis ei pruugi karjumist või argumentide ümberkorraldamist hõlpsalt toetada.
const multiply = (factor, number) => factor * number;
const square = (number) => number * number;
const addTen = (number) => number + 10;
// Funktsioonide ettevalmistamine bind abil
const multiplyByFive = multiply.bind(null, 5);
// Märkus: square ja addTen puhul sobivad need juba malliga.
const complicatedOperation = pipeline(
multiplyByFive, // Võtab numbri, tagastab number * 5
square, // Võtab tulemuse, tagastab (number * 5)^2
addTen // Võtab selle tulemuse, tagastab (number * 5)^2 + 10
);
console.log(complicatedOperation(2)); // (2*5)^2 + 10 = 100 + 10 = 110
console.log(complicatedOperation(3)); // (3*5)^2 + 10 = 225 + 10 = 235
Globaalne rakendamine ja parimad tavad
Toruoperaatorite ja osalise funktsioonirakenduse kontseptsioonid ei ole seotud ühegi konkreetse piirkonna või kultuuriga. Need on arvutiteaduse ja matemaatika põhiprintsiibid, mis muudavad need globaalsete arendajate jaoks universaalselt kohaldatavaks.
Oma koodi internationaliseerimine
Globaalse meeskonnaga töötades või rahvusvahelisele publikule tarkvara arendades on koodi selgus ja prognoositavus esmatähtsad. Toruoperaatori intuitiivne vasakult paremale vool aitab oluliselt mõista keerulisi andmetöötlusi, mis on hindamatu, kui meeskonnaliikmetel võivad olla erinevad keelelised taustad või erinevad JavaScripti idioomide tundmise tasemed.
Näide: rahvusvaheline kuupäeva vormindamine
Kujutagem ette praktilist näidet: kuupäevade vormindamine globaalsele publikule. Kuupäevi saab kogu maailmas esitada paljudes vormingutes (nt MM/PP/AAAA, PP/MM/AAAA, AAAA-PP-PP). Torujuhtme kasutamine võib aidata seda keerukust abstraheerida.
Oletagem, et meil on funktsioon, mis võtab Date objekti ja tagastab vormindatud stringi. Võiksime rakendada transformatsioonide jada: teisendada UTC-ks, seejärel vormindada see spetsiifilisel lokaliseerimistundlikul viisil.
// Oletame, et need on mujal määratletud ja käsitlevad internationaliseerimise keerukust
const toUTCString = (date) => date.toUTCString();
const formatForLocale = (dateString, locale = 'en-US', options = { year: 'numeric', month: 'long', day: 'numeric' }) => {
// Tegelikus rakenduses kasutaks see Intl.DateTimeFormat
// Lihtsuse huvides illustreerime torujuhet
const date = new Date(dateString);
return date.toLocaleDateString(locale, options);
};
const prepareForDisplay = pipeline(
toUTCString, // 1. samm: teisendada UTC stringiks
(utcString) => new Date(utcString), // 2. samm: parsenda tagasi kuupäevaks Intl objekti jaoks
(date) => date.toLocaleDateString('fr-FR', { year: 'numeric', month: 'short', day: '2-digit' }) // 3. samm: vormindada Prantsuse lokaliseerimiseks
);
const today = new Date();
console.log(prepareForDisplay(today)); // Näite väljund (sõltub praegusest kuupäevast): "15 mars 2023"
// Teise lokaliseerimise vormindamiseks:
const prepareForDisplayUS = pipeline(
toUTCString,
(utcString) => new Date(utcString),
(date) => date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })
);
console.log(prepareForDisplayUS(today)); // Näite väljund: "March 15, 2023"
Selles näites loob pipeline korduvkasutatavad kuupäeva vormindamise funktsioonid. Torujuhtme iga samm on eraldiseisev transformatsioon, muutes kogu protsessi läbipaistvaks. Osaline rakendamine on implitsiitne, kui määratleme pipeline sees toLocaleDateString kõne, fikseerides lokaliseerimise ja valikud.
Jõudluse kaalutlused
Kuigi toruoperaatori ja osalise rakendamise selgus ja elegantsus on märkimisväärsed eelised, on mõistlik kaaluda jõudlust. JavaScriptis on funktsioonid nagu reduce ja uute funktsioonide loomine bind või noolfunktsioonide kaudu väheste lisakuludega. Äärmiselt jõudluskriitiliste tsüklite või miljonite kordade jooksul täidetavate toimingute puhul võivad traditsioonilised imperatiivsed lähenemisviisid olla marginaalselt kiiremad.
Siiski, enamiku rakenduste puhul kaaluvad arendaja tootlikkuse, koodi hooldatavuse ja vigade arvu vähenemise eelised tühised jõudlusvahed. Enneaegne optimeerimine on igasuguse kurja juur ja antud juhul on loetavuse kasv märkimisväärne.
Teegid ja raamistikud
Paljud JavaScripti funktsionaalse programmeerimise teegid, nagu Lodash/FP, Ramda ja teised, pakuvad pipe ja partial (või karjumise) funktsioonide robustseid implementatsioone. Kui te juba kasutate sellist teeki, leiate need utiliidid tõenäoliselt hõlpsalt.
Näiteks Ramda kasutades:
const R = require('ramda');
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
// Karjumine on Ramdas levinud, mis võimaldab osalist rakendamist lihtsalt
const addFive = R.curry(add)(5);
const multiplyByThree = R.curry(multiply)(3);
// Ramda'i pipe eeldab funktsioone, mis võtavad ühe argumendi ja tagastavad tulemuse.
// Nii et me saame kasutada meie karjutud funktsioone otse.
const operation = R.pipe(
addFive, // Võtab numbri, tagastab number + 5
multiplyByThree //Võtab tulemuse, tagastab (number + 5) * 3
);
console.log(operation(2)); // (2 + 5) * 3 = 7 * 3 = 21
console.log(operation(10)); // (10 + 5) * 3 = 15 * 3 = 45
Kindlate teekide kasutamine võib pakkuda optimeeritud ja hästi testitud implementatsioone nendele mallidele.
Täiustatud mallid ja kaalutlused
Põhilistest pipe implementatsioonidest kaugemale uurime täiustatud malle, mis jäljendavad veelgi paremini natiivse toruoperaatori potentsiaalset käitumist.
Funktsionaalne värskendusmall
Osaline rakendamine on funktsionaalsete värskenduste rakendamise võti, eriti kui tegeletakse keerukate pesastatud andmestruktuuridega ilma muutmiseta. Kujutage ette kasutaja profiili värskendamist:
const updateUser = (userId, updates) => (users) => {
return users.map(user => {
if (user.id === userId) {
return { ...user, ...updates }; // Ühendage värskendused kasutaja objektiga
} else {
return user;
}
});
};
// Valmistage värskendusfunktsioon osalise rakendamise abil
const updateUserName = (newName) => ({ name: newName });
const updateUserEmail = (newEmail) => ({ email: newEmail });
// Määratlege torujuhe kasutaja värskendamiseks
const processUserUpdate = (userId, updateFn) => {
const updateObject = updateFn;
return pipeline(
updateUser(userId, updateObject)
// Kui seal oleks rohkem järjestikuseid värskendusi, oleksid need siin
);
};
const initialUsers = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
];
// Värskendage Alice'i nimi
const updatedUsersByName = processUserUpdate(1, updateUserName('Alicia'))(initialUsers);
console.log(updatedUsersByName);
// Värskendage Bobi e-posti
const updatedUsersByEmail = processUserUpdate(2, updateUserEmail('bob.updated@example.com'))(initialUsers);
console.log(updatedUsersByEmail);
// Ühendage värskendused sama kasutaja jaoks
const updatedAlice = pipeline(
updateUser(1, updateUserName('Alicia')),
updateUser(1, updateUserEmail('alicia.new@example.com'))
)(initialUsers);
console.log(updatedAlice);
Siin on updateUser funktsioonitehas. See tagastab funktsiooni, mis teostab värskenduse. userId ja spetsiifilise värskendusloogika (updateUserName, updateUserEmail) osalise rakendamisega loome väga spetsialiseeritud värskendusfunktsioonid, mis sobivad torujuhtmesse.
Punktivaba stiiliga programmeerimine
Toruoperaatori ja osalise rakendamise kombinatsioon viib sageli punktivaba stiilini programmeerimist, tuntud ka kui taktiilne programmeerimine. Selles stiilis kirjutate funktsioone, kompositsioneerides teisi funktsioone, ja väldite töötavat andmestruktuuri (punktid) otsest mainimist.
Vaatame meie pipeline näidet:
const addPrefix = (prefix, str) => `${prefix}${str}`;
const toUpperCase = (str) => str.toUpperCase();
const addSuffix = (str, suffix) => `${str}${suffix}`;
const addProcessedPrefix = (str) => addPrefix('processed_', str);
const addFinalSuffix = (str) => addSuffix(str, '_final');
const processPipeline = pipeline(
addProcessedPrefix,
toUpperCase,
addFinalSuffix
);
// Siin on 'processPipeline' funktsioon, mis on määratletud ilma otseselt
// mainimata andmeid, millega see töötab. See on teiste funktsioonide kompositsioon.
See võib muuta koodi väga lühikeseks, kuid võib olla ka raskemini loetav neile, kes pole funktsionaalse programmeerimisega tuttavad. Peamine on saavutada tasakaal, mis parandab teie meeskonna loetavust.
|> ` Operaator: eelvaade
Kuigi see on endiselt ettepanek, võib toruoperaatori kavandatud süntaksi mõistmine teavitada, kuidas me täna oma koodi struktureerime. Ettepanekul on kaks vormi:
- Edasiliikumise toru (
|>): Nagu arutati, on see kõige levinum vorm, mis edastab väärtust vasakult paremale. - Tagurpidi toru (
#): Vähem levinud variant, mis edastab väärtuse paremal asuva funktsiooni viimase argumendina. Seda vormi on vähem tõenäoline selle praeguses olekus vastu võtta, kuid see rõhutab selliste operaatorite kavandamise paindlikkust.
Toruoperaatori lõplik kaasamine JavaScripti tõenäoliselt julgustab rohkem arendajaid kasutama funktsionaalseid malle, nagu osaline rakendamine, et luua väljendusrikast ja hooldatavat koodi.
Järeldus
JavaScripti toruoperaator, isegi selle kavandatud olekus, pakub veenvat visiooni puhtama, loetavama koodi jaoks. Mõistmise ja selle põhiprintsiipide rakendamise abil, kasutades tehnikaid nagu osaline funktsioonirakendus, saavad arendajad oluliselt parandada oma võimet kompositsioneerida keerukaid toiminguid.
Olenemata sellest, kas simuleerite toruoperaatorit abifunktsioonidega nagu pipe või kasutate teeke, on eesmärk muuta teie kood loogiliseks ja seda oleks lihtsam mõista. Võtke omaks need funktsionaalse programmeerimise paradigmata, et kirjutada robustsemat, hooldatavamat ja elegantsemat JavaScripti, mis seab teid ja teie projekte edukaks globaalsel areenil.
Alustage nende mallide kaasamisega oma igapäevasesse kodeerimisse. Katsetage bind, noolfunktsioonide ja kohandatud pipe funktsioonidega. Teekond funktsionaalsema ja deklaratiivsema JavaScripti poole on tasuv.