sandra-larsson
Educator Fullstack Developer Javascript at Chas Academy
FJS26 · V46 · ONSDAG
Igår gömde vi datan i objekt. Idag låter vi den flöda genom funktioner.
Chas Academy · Onsdag 11 november 2026
Igår svarade OOP på frågan var bor datan, och vem får ändra den? Idag är det den funktionella stilens tur.
Det här kan ni redan. Gissa först.
UPPVÄRMNING
Gissa först. Får vi något nytt, eller ändrar vi originalet?
const a = { x: 1, y: 2 };
const b = { ...a, y: 3 };
console.log(a.y, b.y);const prices = [59, 35]; const lower = prices.map((p) => p - 10); console.log(prices, lower);
const items =
['kaffe', 'bulle'];
const same = items;
same.push('te');
console.log(items.length);Ett paradigm där programmet byggs av funktioner som tar in data och returnerar ny data, i stället för att ändra den som finns.
FUNKTIONELL STIL
FUNKTIONELL STIL
Allt en funktion gör utöver att returnera ett värde.
Muterar indata (push, sort), ändrar en global variabel, skriver till localStorage.
console.log, rör DOM:en, fetch.
Math.random(), new Date(), läser en global som kan ändras. Inte en sidoeffekt i sig, men gör funktionen oren.
Övning
Öppna 2-ren-eller-oren/js/main.js. Skriv REN eller OREN ovanför var och en av de åtta funktionerna, och varför. Skriv sedan om två av de orena så att de blir rena.
10 min
Du kan avgöra om en funktion är ren, och göra en oren funktion ren genom att ta in det som ändras som parameter.
Målet är inte noll sidoeffekter. Ett program utan dem gör ingenting. Målet är att samla dem på få ställen.
FUNKTIONELL STIL
FUNKTIONELL STIL
En higher-order function tar emot eller returnerar en funktion. Ni har skrivit dem sedan v44.
const isCheap = (item) => item.price < 40; items.filter(isCheap);
const cheaperThan = (limit) => (item) => item.price < limit; items.filter(cheaperThan(40));
FUNKTIONELL STIL
Namnen på drycker under 50 kr, sorterade.
const names = [];
for (const item of items) {
if (item.category === 'dryck'
&& item.price < 50) {
names.push(item.name);
}
}
names.sort();const names = items .filter((i) => i.category === 'dryck') .filter((i) => i.price < 50) .map((i) => i.name) .sort();
Hur funktionell kod håller tillstånd utan klasser.
En funktion som minns variablerna i scopet där den skapades, även efter att den yttre funktionen har kört klart.
CLOSURES
const makeGreeter = (greeting) => {
return (name) =>
`${greeting}, ${name}!`;
};
const hej = makeGreeter('Hej');
const hello = makeGreeter('Hello');makeGreeter har kört klart. Ändå vet hej att greeting är 'Hej'.
hej('Ada'); // 'Hej, Ada!'
hello('Ada'); // 'Hello, Ada!'CLOSURES
CLOSURES
const createCounter = () => {
let count = 0;
return {
increase: () => { count++; },
value: () => count,
};
};const a = createCounter(); const b = createCounter(); a.increase(); a.increase(); b.increase(); a.value(); // 2 b.value(); // 1 a.count; // undefined
CLOSURES
Tisdagens klass, och samma API med en closure.
class TodoList {
#todos = [];
add(text) {
this.#todos.push(text);
}
get all() {
return [...this.#todos];
}
}
const list = new TodoList();const createTodoList = () => {
let todos = [];
return {
add: (text) => {
todos = [...todos, text];
},
all: () => [...todos],
};
};
const list = createTodoList();CLOSURES
Klassen: #todos. Closuren: en vanlig variabel som bara funktionerna ser.
Klassens metoder tappar this om du lyfter ut dem. Closurens funktioner använder inget this, så const { add } = list fungerar.
Bara klassen. Behöver du instanceof eller arv är klassen naturligare.
För den som använder objektet är skillnaden liten. Välj det teamet läser lättast, och var konsekvent.
CLOSURES
for (const song of songs) {
const button =
document.createElement('button');
button.textContent = song;
button.addEventListener('click',
() => console.log(song));
main.append(button);
}Loopen har kört klart långt innan någon klickar. Varje varv har ett eget song, och varje callback minns sitt. Det är en closure.
Gör en imperativ kassa ren.
KASSAN
Kör checkout('STUDENT') två gånger med samma varor. Gissa först.
let total = 0;
export function checkout(code) {
for (...) {
if (discountCodes[code]) {
product.price = product.price
* (1 - discountCodes[code]);
}
line.sum = product.price * line.quantity;
total = total + line.sum;
}
console.log(receiptText);
}Hitta minst fyra saker innan du läser vidare i noterna.
Övning
Skriv rena funktioner i js/receipt.js i repot js--fp-kassan: findProduct, lineTotal, subtotal, applyDiscount, vatOf och formatReceipt. Ingen av dem får ändra products eller lines, läsa globala variabler eller logga. Använd dem i main.js, som är det enda stället med sidoeffekter.
15 min
Samma köp ger samma kvitto, hur många gånger du än kör.
KASSAN
export const lineTotal = (line, products) =>
findProduct(products, line.productId)
.price * line.quantity;
export const subtotal = (lines, products) =>
lines.reduce(
(sum, line) => sum + lineTotal(line, products),
0,
);
export const applyDiscount = (amount, code, codes) =>
amount * (1 - (codes[code] || 0));console.log(
formatReceipt(lines, products, 'STUDENT',
discountCodes, VAT_RATE),
);KURSMÅL 3
Saker med eget tillstånd som lever en stund och ändras: en spelare, en kortlek, en anslutning. När du vill ha instanceof, arv eller ett tydligt API runt datan.
Data som omvandlas: listor som filtreras, räknas om och formateras. Logik som ska testas. Det mesta i en frontend som visar data.
Rena funktioner för beräkningarna, och ett objekt eller en closure för det tillstånd som måste finnas. Kassan är ett exempel.
OOP gömmer mutationen bakom metoder. Funktionell stil undviker den, och samlar det som ändå måste ändras på ett ställe.
EFTER PASSET
Ta med två egna lösningar. På torsdag granskar ni varandras kod i par.
js--fp-kassan): createCart(products) i js/cart.js, med add, remove, lines() och total(). Raderna bor i en closure. Inga push, inga splice.main.js får röra DOM:en.js--oop-poker): minst del 1–4, från tisdagen.EFTER PASSET
By sandra-larsson