Denna JavaScript-språkfunktion kan hjälpa till att städa upp din kod och kommer att ge dig en ny uppfattning om hur funktioner fungerar.
Curried-funktioner kan hjälpa till att göra din JavaScript-kod mer läsbar och uttrycksfull. Currytekniken är idealisk när du vill bryta ner komplex logik i mindre, fristående, mer hanterbara bitar av kod.
Lär dig allt om curry-funktioner i JavaScript, hur du använder funktionscurry-tekniken för att skapa delvis tillämpade funktioner, såväl som verkliga användningsfall för både curryfunktioner och delvis tillämpade funktioner.
Vad är curry?
Currying är uppkallad efter matematikern Haskell B. Curry, och konceptet härrör från Lambda-kalkyl. Currying tar en funktion som tar emot mer än en parameter och delar upp den i en serie unära (en-parameter) funktioner. Med andra ord, en curried funktion tar bara en parameter åt gången.
Ett grundläggande exempel på curry
Nedan är ett exempel på en curryfunktion:
functionbuildSandwich(ingredient1) {
return(ingredient2) => {
return(ingredient3) => {
return`${ingredient1},${ingredient2},${ingredient3}`
}
}
}
De buildSandwich() funktion returnerar en annan funktion — en anonym funktion som tar emot ingrediens2 argument. Sedan returnerar denna anonyma funktion en annan anonym funktion som tar emot ingrediens3. Slutligen returnerar denna sista funktion mallen bokstavlig, ett sätt att formateringssträngar i JavaScript.
Det du har skapat är en kapslad funktion där varje funktion anropar den under den tills vi når slutet. Nu när du ringer buildSandwich() och skickar den en enda parameter, returnerar den den del av funktionen vars argument du ännu inte har tillhandahållit:
console.log(buildSandwich("Bacon"))
Du kan se från utgången att buildSandwich returnerar en funktion:
För att slutföra funktionsanropet måste du ange alla tre argument:
buildSandwich("Bacon")("Lettuce")("Tomato")
Denna kod skickar "Bacon" till den första funktionen, "Sallat" till den andra och "Tomat" till den sista funktionen. Med andra ord buildSandwich() funktion är egentligen uppdelad i tre funktioner, där varje funktion endast får en parameter.
Även om det är fullt giltigt att använda curry med de traditionella funktionerna, kan all häckning bli ganska ful ju djupare du kommer. För att komma runt detta kan du använda pilfunktioner och dra nytta av deras renare syntax:
const buildMeal = ingred1 =>ingred2 =>ingred3 =>
`${ingred1}, ${ingred2}. ${ingred3}`;
Denna omstrukturerade version är mer kortfattad, en fördel med att använda pilfunktioner kontra vanliga funktioner. Du kan anropa funktionen på samma sätt som du gjorde med den föregående:
buildMeal("Bacon")("Lettuce")("Tomato")
Delvis tillämpade curryfunktioner
Delvis tillämpade funktioner är en vanlig användning av curry. Denna teknik innebär att endast de nödvändiga argumenten tillhandahålls åt gången (istället för att tillhandahålla alla argument). När du anropar en funktion genom att skicka alla nödvändiga parametrar, säger du att du har "tillämpat" den funktionen.
Låt oss titta på ett exempel:
const multiply = (x, y) => x * y;
Nedan är curryversionen av multiplicera:
const curriedMultiply = x =>y => x * y;
De curriedMultiply() funktionen tar emot x argument för den första funktionen och y för den andra funktionen multiplicerar den båda värdena.
För att skapa den första delvis tillämpade funktionen, ring curriedMultiple() med den första parametern och tilldela den returnerade funktionen till en variabel:
const timesTen = curriedMultiply(10)
Vid denna tidpunkt har koden "delvis tillämpat" den curriedMultiply() fungera. Så när du vill ringa gånger Tio(), du behöver bara skicka ett nummer och numret multipliceras automatiskt med 10 (som lagras i den tillämpade funktionen):
console.log(timesTen(8)) // 80
Detta låter dig bygga på en enda komplex funktion genom att skapa flera anpassade funktioner från den, var och en med sin egen funktionalitet inlåst.
Ta en titt på ett exempel som är närmare ett riktigt användningsfall för webbutveckling. Nedan har du en uppdateraElemText() funktion som tar ett elements id vid det första samtalet, innehållet i det andra samtalet, och sedan uppdaterar elementet baserat på id och innehåll du tillhandahöll det:
const updateElemText = id = content
=> document.querySelector(`#${id}`).textContent = content// Lock the element's id into the function:
const updateHeaderText = updateElemText('header')
// Update the header text
updateHeaderText("Hello World!")
Funktionssammansättning Med Curried Funktioner
En annan vanlig användning av curry är funktionssammansättning. Detta låter dig anropa små funktioner, i en specifik ordning, och kombinera dem till en enda, mer komplex funktion.
Till exempel, i en hypotetisk e-handelswebbplats, här är tre funktioner som du kanske vill köra efter varandra (i exakt ordning):
const addCustomer = fn =>(...args) => {
console.log("Saving customer info")
return fn(...args)
}const processOrder = fn =>(...args) => {
console.log(`processing order #${args[0]}`)
return fn(...args);
}
let completeOrder = (...args) => {
console.log(`Order #${[...args].toString()} completed.`);
}
Observera att den här koden använder låta nyckelord för att definiera komplett beställning() fungera. Detta gör att du kan tilldela ett värde till variabeln och är en del av hur scoping fungerar i JavaScript.
Därefter måste du anropa funktionerna i omvänd ordning (inifrån och ut) eftersom du vill lägga till kunderna först:
completeOrder = (processOrder(completeOrder));
completeOrder = (addCustomer(completeOrder));
completeOrder("1000")
Detta ger dig följande utdata:
Om du skulle skriva ovanstående funktioner på vanligt sätt kommer koden att se ut ungefär så här:
functionaddCustomer(...args) {
returnfunctionprocessOrder(...args) {
returnfunctioncompleteOrder(...args) {
// end
}
}
}
När du ringer till addCustomer() funktion och skicka in argumenten, du börjar inifrån och arbetar dig ut till toppen av funktionen.
Konvertera en normal funktion till en curry-funktion med en curry-funktion
Om du planerar att använda curryfunktioner mycket kan du effektivisera processen med en hjälparfunktion.
Denna funktion kommer att konvertera alla normala funktioner till en curry funktion. Den använder rekursion för att hantera valfritt antal argument.
const curry = (fn) => {
return curried = (...args) => {
if (fn.length !== args.length) {
return curried.bind(null, ...args)
}
return fn(...args);
}
}
Denna funktion kommer att acceptera alla skrivna standardfunktioner som tar emot mer än en parameter, vilket returnerar en curry version av den funktionen. För att se det i aktion, använd den här exempelfunktionen som tar tre parametrar och lägger till dem:
const total = (x, y, z) => x + y + z
För att konvertera den här funktionen, anropa curry() funktion och godkänt total som argument:
const curriedTotal = curry(total)
För att nu anropa funktionen behöver du bara skicka in alla argument:
console.log(curriedTotal(10)(20)(30)) // 60
Mer om funktioner i JavaScript
JavaScripts funktioner är extremt flexibla och curryfunktioner är bara en liten del av det. Det finns många andra typer av funktioner som pilfunktioner, konstruktorfunktioner och anonyma funktioner. Att bekanta dig med dessa funktioner och deras komponenter är nyckeln till att behärska JavaScript.