map() je
metoda
nad Array objektom koja izvršava zadanu transformaciju nad svakim elemetom u
array-u i vraća novi array sa transformiranim elementima.
Konvertiranje svih stringova u array-u u uppercase:
const pets = ['dog', 'snake', 'cat', 'hamster'];
const loudPets = pets.map(pet => pet.toUpperCase());
console.log(loudPets);Za usporedbu, ista operacija koristeći for petlju:
const pets = ['dog', 'snake', 'cat', 'hamster'];
const loudPets = [];
for (let i = 0; i < pets.length; i++) {
loudPets.push(pets[i].toUpperCase());
}
console.log(loudPets);Prednosti:
- kraća sintaksa
- manje logike (za iteriranje po array-u se interno brine
map()metoda) - jasniji kôd
- ne modificira originalni array
- dopušta method chaining sa drugim array metodama
filter() je
metoda
nad Array objektom koja vraća novi array sastavljen samo od elemenata
originalnog array-a koji zadovoljavaju test koji implementira zadana funkcija.
Želimo filtrirati samo mačke iz polja kućnih ljubimaca:
const pets = [
{ name: 'Doug', type: 'dog' },
{ name: 'Keanu', type: 'cat' },
{ name: 'Ghost', type: 'direwolf' },
{ name: 'Stevens', type: 'cat' },
];
const cats = pets.filter(pet => pet.type === 'cat');
console.log(cats);Implementacija iste funkcionalnosti koristeći for petlju:
const pets = [
{ name: 'Doug', type: 'dog' },
{ name: 'Keanu', type: 'cat' },
{ name: 'Ghost', type: 'direwolf' },
{ name: 'Stevens', type: 'cat' },
];
const cats = [];
for (let i = 0; i < pets.length; i++) {
if (pets[i].type === 'cat') {
cats.push(pets[i]);
}
}
console.log(cats);Novi array ima samo one elemente za koje test funkcija vraća true.
Prednosti: iste kao i za map() metodu.
Swiss army knife array funkcija. Izvršava tzv. reducer funkciju nad svakim
elementom zadanog array-a i vraća rezultat.
Reducer - funkcija koja prima do četiri vrijednosti, redom:
- Accumulator
- Trenutnu vrijednost
- Trenutni index
- Izvorni array
Vrijednost koju reducer vraća postaje novi Accumulator koji se prosljeđuje u
reducer u idućoj iteraciji. Vrijednost accumulatora koju reducer vrati u zadnjoj
iteraciji postaje povratna vrijednost reduce() metode.
Početna vrijednost accumulatora - vrijednost koja se šalje kao accumulator u
reducer funkciju u prvoj iteraciji. Ako nije zadana, koristi se prvi element
array-a.
const numbers = [1, 2, 3, 4, 5];
const sumOfNumbers = numbers.reduce(
(accumulator, value) => accumulator + value,
0,
);
console.log(sumOfNumbers); // 15reduce() je moćan alat s kojim možemo napraviti i naše implementacije map()
i filter() metoda.
const pets = ['dog', 'snake', 'cat', 'hamster'];
function map(array, callback) {
return array.reduce((accumulator, value) => {
accumulator.push(callback(value));
return accumulator;
}, []);
}
const loudPets = map(pets, value => value.toUpperCase());
console.log(loudPets);const pets = [
{ name: 'Doug', type: 'dog' },
{ name: 'Keanu', type: 'cat' },
{ name: 'Ghost', type: 'direwolf' },
{ name: 'Stevens', type: 'cat' },
];
function filter(array, testCallback) {
return array.reduce((accumulator, value) => {
if (testCallback(value)) {
accumulator.push(value);
}
return accumulator;
}, []);
}
const cats = filter(pets, pet => pet.type === 'cat');
console.log(cats);const pets = ['dog', 'snake', 'dog', 'cat', 'hamster', 'cat', 'cat', 'snake'];
const oneOfEachPet = pets.reduce((accumulator, pet) => {
if (accumulator.indexOf(pet) === -1) {
accumulator.push(pet);
}
return accumulator;
}, []);
console.log(oneOfEachPet);Currying je tehnika pozivanja funkcije gdje funkcija ne prima sve argumente odjednom. Umjesto toga prima prvi argument i vraća novu funkciju koja prima drugi argument i vraća novu funkciju (ili rezultat ako se radi o funkciji sa dva argumenta), itd...
Na primjer, gdje običnu add funkciju pozivamo ovako:
add(1, 2, 3);curried verzija bi se pozivala ovako:
add(1)(2)(3);Primjer implementacije:
function add(a, b) {
return a + b;
}
function curriedAdd(a) {
return function(b) {
return a + b;
};
}
console.log(add(5, 3));
console.log(curriedAdd(5)(3));Funkcija curriedAdd() se može kraće napisati koristeći arrow funkcije:
const curriedAdd = a => b => a + b;
console.log(curriedAdd(5)(3));Currying je koristan za pripremu argumenata unaprijed za npr. map, filter, reduce, event handlere i sl.
const pets = [
{ name: 'Doug', type: 'dog' },
{ name: 'Keanu', type: 'cat' },
{ name: 'Ghost', type: 'direwolf' },
{ name: 'Stevens', type: 'cat' },
];
const isType = type => pet => pet.type === type;
const cats = pets.filter(isType('cat'));
const direwolfs = pets.filter(isType('direwolf'));
console.log('cats', cats);
console.log('direwolfs', direwolfs);Dependency injection je jednostavna tehnika koju koristimo u objektno orijentiranom programiranju gdje objektu kroz konstruktor dajemo druge objekte (tzv. ovisnosti) koji mu trebaju. Ovo radimo kako bi naš kôd učinili lakšim za održavanje, reuse i testiranje.
Dependency injection u funkcionalnom programiranju nam olakšava bind() metoda. S ovom tehnikom možemo neke argumente funkciji zadati unaprijed bez da ju pozovemo.
Recimo da želimo implementirati metodu koja vraća ime korisnika za zadani id.
Ova metoda vrši ajax poziv
na server i dohvaća ime korisnika iz server response-a. U primjerima koristimo
jQuery .get() metodu za izvršavanje ajax
poziva.
Primjer bez dependecy injection:
function getUserName(id) {
return $.get(`https://www.example.com/users/${id}`).then(user => user.name);
}
getUserName(42);Gornji primjer nije testabilan, jer u unit testovima ne želimo zaista vršiti
ajax pozive. Kako bi rješili ovaj problem, promijeniti ćemo getUserName()
funkciju da prima dva parametra, gdje je prvi ajax funkcija koju će koristiti:
function getUserName($get, id) {
return $get(`https://www.example.com/users/${id}`).then(user => user.name);
}
getUserName($.get, 42);Unit test sada može funkciji getUserName() dati svoju implementaciju $.get
koja ne vrši ajax poziv.
Naravno, ne želimo svaki put kada koristimo ovu funkciju u produkciji slati i
$.get parametar, pa ćemo za produkciju definirati novu funkciju
makeGetUserName() koja vraća getUserName sa unaprijed zadanim $get
parametrom:
// Ovu funkciju koristimo u produkciji
export default function makeGetUserName($get) {
// Prvi argument bind funkcije definira vrijednost od this (nije bitan jer ga
// getUserName() ne koristi)
return getUserName.bind(null, $get);
}
// Ovu funkciju koristimo u testovima
export function getUserName($get, id) {
return $get(`https://www.example.com/users/${id}`).then(user => user.name);
}Ako smo sigurni da ćemo uvijek koristiti $.get, možemo definirati wrapper
funkciju koja nekonfiguriranoj funkciji proslijeđuje $.get kao prvi parametar.
// Ovu funkciju koristimo u produkciji
export default function getUserName(id) {
return getUserNameUnconfigured($.get, id);
}
// Ovu funkciju koristimo u testovima
export function getUserNameUnconfigured($get, id) {
return $get(`https://www.example.com/users/${id}`).then(user => user.name);
}