Mörkt tema
Loopar (35 min)
💡 Introduktion till ämnet (flipped classroom)
⏰ Tidsåtgång: c.a. 35 min
Loopar (14 min)
Lägga till nya kryddor (12 min)
Radera kryddor (9 min)
🔈 Transkription
Vi fortsätter med det virtuella kryddskåpet. Såhär ser vår array ut efter att vi har sorterat den i bokstavsordning:
js
const mySpices = ['Cayenne', 'Cinnamon', 'Garlic Powder', 'Pepper'];1
Nu vill vi skriva ut den som en lista i HTML-dokumentet.
- Vi skapar en platshållare (en
<ul>i detta fallet) för att fylla på med våra kryddor. - Sedan lägger vi till "child elements" i vår kryddhylla.
html
<ul id="mySpiceRack"></ul>1
js
const mySpices = ['Cayenne', 'Cinnamon', 'Garlic Powder', 'Pepper'];
const spiceRack = document.querySelector('#mySpiceRack');
const spice1Name = mySpices[0]; // Namnet på kryddan
const spice1Node = document.createElement('li'); // Skapa ett <li>-element
const spiceTextNode = document.createTextNode(spice1Name); // Skapa en textnod (tänk dig att det är som ett <span>)
spice1Node.appendChild(spiceTextNode); // Nu står det <li>Cayenne</li>
spiceRack.appendChild(spice1Node); // Lägg till i <ul>-listan.1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Toppen, nu har vi fått in Cayenne i listan. Då lägger vi in Cinnamon:
js
const mySpices = ['Cayenne', 'Cinnamon', 'Garlic Powder', 'Pepper'];
const spiceRack = document.querySelector('#mySpiceRack');
const spice1Name = mySpices[0]; // Namnet på kryddan
const spice1Node = document.createElement('li'); // Skapa ett <li>-element
const spice1TextNode = document.createTextNode(spice1Name); // Skapa en textnod (tänk dig att det är som ett <span>)
spice1Node.appendChild(spice1TextNode); // Nu står det <li>Cayenne</li>
spiceRack.appendChild(spice1Node); // Lägg till i <ul>-listan.
// Lägg till cinnamon
const spice2Name = mySpices[1];
const spice2Node = document.createElement('li');
const spice2TextNode = document.createTextNode(spice2Name);
spice2Node.appendChild(spice2TextNode);
spiceRack.appendChild(spice2Node);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Och så kör vi för "Garlic Powder":
js
const mySpices = ['Cayenne', 'Cinnamon', 'Garlic Powder', 'Pepper'];
const spiceRack = document.querySelector('#mySpiceRack');
const spice1Name = mySpices[0]; // Namnet på kryddan
const spice1Node = document.createElement('li'); // Skapa ett <li>-element
const spice1TextNode = document.createTextNode(spice1Name); // Skapa en textnod (tänk dig att det är som ett <span>)
spice1Node.appendChild(spice1TextNode); // Nu står det <li>Cayenne</li>
spiceRack.appendChild(spice1Node); // Lägg till i <ul>-listan.
// Lägg till cinnamon
const spice2Name = mySpices[1];
const spice2Node = document.createElement('li');
const spice2TextNode = document.createTextNode(spice2Name);
spice2Node.appendChild(spice2TextNode);
spiceRack.appendChild(spice2Node);
// Lägg till vitlökspulver
const spice3Name = mySpices[2];
const spice3Node = document.createElement('li');
const spice3TextNode = document.createTextNode(spice3Name);
spice3Node.appendChild(spice3TextNode);
spiceRack.appendChild(spice3Node);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Fint, då är det bara peppar kvar!
js
const mySpices = ['Cayenne', 'Cinnamon', 'Garlic Powder', 'Pepper'];
const spiceRack = document.querySelector('#mySpiceRack');
const spice1Name = mySpices[0]; // Namnet på kryddan
const spice1Node = document.createElement('li'); // Skapa ett <li>-element
const spice1TextNode = document.createTextNode(spice1Name); // Skapa en textnod (tänk dig att det är som ett <span>)
spice1Node.appendChild(spice1TextNode); // Nu står det <li>Cayenne</li>
spiceRack.appendChild(spice1Node); // Lägg till i <ul>-listan.
// Lägg till cinnamon
const spice2Name = mySpices[1];
const spice2Node = document.createElement('li');
const spice2TextNode = document.createTextNode(spice2Name);
spice2Node.appendChild(spice2TextNode);
spiceRack.appendChild(spice2Node);
// Lägg till vitlökspulver
const spice3Name = mySpices[2];
const spice3Node = document.createElement('li');
const spice3TextNode = document.createTextNode(spice3Name);
spice3Node.appendChild(spice3TextNode);
spiceRack.appendChild(spice3Node);
// Lägg till peppar
const spice4Name = mySpices[3];
const spice4Node = document.createElement('li');
const spice4TextNode = document.createTextNode(spice4Name);
spice4Node.appendChild(spice4TextNode);
spiceRack.appendChild(spice4Node);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Men, finns det inte något lite smartare sätt att göra detta än att upprepa samma kod om och om igen? 😉
Om du tittar på koden ovan, så gör vi ju i princip samma sak om och om igen - det enda som varierar är en siffra! Kan vi inte automatisera det på något sätt?
Jodå, loops to the rescue!
js
for(let i = 0; i < 10; i++) {
console.log(i);
}1
2
3
2
3
Ovan kod skulle skapa en variabel som börjar på 0 (let i = 0). i är vanligt att använda i loopar och står för "index", men programmerare är lata och orkar inte skriva ut hela ordet.
I nästa block, i < 10, så anger vi villkoret för hur många gånger loopen ska köras, i detta fall så länge i är mindre än 10.
Och i det sista blocket anger vi vad som händer mellan varje loop-varv/iteration: i det här fallet ska i öka med 1, vilket vi kan skriva som i++ (men det hade gått lika bra att skriva i += 1).
Om vi nu försöker automatisera vår kryddutskrift då. Vi börjar med att identifiera vilken kod som upprepas, och lägger den inne i en loop:
js
const mySpices = ['Cayenne', 'Cinnamon', 'Garlic Powder', 'Pepper'];
const spiceRack = document.querySelector('#mySpiceRack');
for (let i = 0; i < 10; i++) {
const spice2Name = mySpices[1];
const spice2Node = document.createElement('li');
const spice2TextNode = document.createTextNode(spice2Name);
spice2Node.appendChild(spice2TextNode);
spiceRack.appendChild(spice2Node);
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Men, nu behöver vi få koden/utskriften att variera beroende på vilket index/iteration vi befinner oss i, i loopen:
js
const mySpices = ['Cayenne', 'Cinnamon', 'Garlic Powder', 'Pepper'];
const spiceRack = document.querySelector('#mySpiceRack');
for (let i = 0; i < 10; i++) {
const spice2Name = mySpices[i];
const spice2Node = document.createElement('li');
const spice2TextNode = document.createTextNode(spice2Name);
spice2Node.appendChild(spice2TextNode);
spiceRack.appendChild(spice2Node);
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Däremot har vi nu några kryddor som heter undefined i vår kryddhylla. Öh, sådana vill vi inte ha. Vi vill att loopen automatiskt ska gå så många gånger som vi har kryddor i vår kryddlista, så vi ändrar från 10 varv till mySpices.length. På så vis, om vi lägger till eller tar bort kryddor, så anpassar sig loopen automatiskt utifrån det:
js
const mySpices = ['Cayenne', 'Cinnamon', 'Garlic Powder', 'Pepper'];
const spiceRack = document.querySelector('#mySpiceRack');
for (let i = 0; i < mySpices.length; i++) {
const spice2Name = mySpices[i];
const spice2Node = document.createElement('li');
const spice2TextNode = document.createTextNode(spice2Name);
spice2Node.appendChild(spice2TextNode);
spiceRack.appendChild(spice2Node);
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Vi kan passa på att städa upp lite i namngivningen, och optimera koden ytterligare:
js
const mySpices = ['Cayenne', 'Cinnamon', 'Garlic Powder', 'Pepper'];
const spiceRack = document.querySelector('#mySpiceRack');
for (let i = 0; i < mySpices.length; i++) {
const spiceItem = document.createElement('li');
const spiceName = document.createTextNode(mySpices[i]);
spiceItem.appendChild(spiceName);
spiceRack.appendChild(spiceItem);
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Lite lättare att läsa! Och koden har gått från 30 rader, till 9!
💬 Kodexempel
I modulen Event pratade vi lite om s.k. "anonyma funktioner". Om vi tar ett exempel här:
js
const btns = document.querySelectorAll('button'); // Välj alla <button>-element i HTML-dokumentet
// Loopa igenom alla knappar
btns.forEach((btn) => {
// Tilldela varje knapp en event-lyssnare (klick)
btn.addEventListener('click', () => {
// Uppdate priset vid varje knapptryck
btn.parentElement.querySelector('.price').innerHTML = (Number(btn.parentElement.querySelector('.amount').innerHTML) + 1) * 25;
});
});1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Problement med ovan kod blir att vi för varje knapp skapar en ny funktion istället för att återanvända en funktion. Jämför det med t.ex. en desinficerande engångsservett respektive återanvändbar tvätt-duk.
Garbage collection
Det verkar ju väldigt oekonomiskt och miljö-o-vänligt att ta en ny servett varje gång vi behöver torka av köksbordet, jämfört med att "skölja av" och torka en tvättduk (återställa och återanvända funktionen).
Varje gång vi skapar en anonym funktion så är det som att ta en engångs-servett i bruk; den behöver direkt kastas efter användning (hamnar i garbage collection/"memory garbage collection").
Vad är då garbage collection?
Datorn har en viss mängd arbetsminne, säg 8 GB. Av den kanske webbläsaren (Chrome/Firefox/nåt annat) tillåts använda 4 MB. Varje gång du besöker en webbplats så används det upp en viss mängd minne; det laddas in annonser, det körs lite JavaScript, det laddas in bilder, osv. Varje sådan "operation" kräver en viss mängd minne.
Vi behöver ett sätt att hålla reda på vad som inte längre är relevant. Vi vill ha en sopbil till vår användning som hämtar upp sopor och frigör vårt minne.
Detta kallas för (memory) garbage collection - ofta förkortat GC i programmeringssammanhang.
Tänk dig att det kommer en sopbil körandes 🚚 som samlar upp all kod som inte längre är nödvändig för att hålla datorns minne rent. Detta för att allt ska funka så snabbt och smidigt som möjligt.
Låt oss säga att datorn har ett maximalt minne på 500 MB, och att varje funktion tar upp 1 MB. Nej, det är inte 100% verklighetsförankrat, men skitsamma.
Om vi nu har en loop, t.ex. såhär:
js
const btns = document.querySelectorAll('button'); // HTML-filen innehåller 100 knappar
for(let i = 0; i < btns.length; i++) {
btns.addEventListener('click', () => {
// do something on button click
});
}1
2
3
4
5
6
2
3
4
5
6
I kodexemplet ovan har vi skapat då 100 stycken nya, anonyma funktioner. Varje funktion var 1 MB, så vi har alltså skapat 1 MB * 100 => 100 MB belastning på datorns minne. Vi har använt 100/500 MB (20 %) av det arbetsminne vi har att tillgå.
Om vi istället skapar en funktion så har vi istället skapat betydligt mindre belastning för datorn, 1 MB/500 MB (0,2 %):
js
const btns = document.querySelectorAll('button'); // HTML-filen innehåller 100 knappar
for(let i = 0; i < btns.length; i++) {
btns.addEventListener('click', myFunction);
}
function myFunction() { // återanvänd samma funktion flera gånger
// do something on button click
}1
2
3
4
5
6
7
2
3
4
5
6
7
Inom "computer science" brukar detta räknas på som "Big O" eller "Ordo N" - man räknar på hur snabba program är. För den som vill fördjupa sig, se resurser längre ner.
📖 Läsanvisningar
- JavaScript - The Definitive Guide: 5.4 - Loops
- JavaScript - The Definitive Guide: 5.5 - Jumps
🏋️ Övningsuppgifter
✅ Avstämning
Använd denna lista för att "checka av" att du förstår modulens koncept.
Terminologi
- do/while
- while
- for
- for in, of
Du är klar när…
- Du kan namnge 3 olika typer av loopar
- Du kan loopa igenom en lista
- Du vet vad en loop gör och när du ska använda den