# Practice session

A recap of period 1, the ten exercises of term 1 and the Code Quest Academy.

Multimedia & Creative Technology · 2.1 Webcoding · Lesson 5 · version 1.0.0 · 2026-10-18

Tags: javascript

Slides: <https://slides.mctantwerp.be/en/webcoding/05-practice-session>

<!-- slide 2 -->

## After this lesson you can

- Set up an exercise with an index.html and a script.js, and find out why the script does not load
- Turn the answer of a prompt into a number, and check whether it really is one
- Write functions that take parameters and give back a result with return
- Walk through an array or an array of objects to show, filter, add up or find the highest value
- Repeat something with a pause in between, with setTimeout
- Solve the six exercises of term 1 on your own, and the four bonus ones if you want more

> **Explanation:** These are the skills the laptop exam in January asks for in the part about programming in JavaScript. Not the syntax by heart, the exam is open book, but knowing which piece you need and how to put the pieces together. That only comes from writing a lot of small programs, which is the point of today.

<!-- slide 3 -->

## Today

- **[01 Term 1 exercises](https://term1.webcoding.learn.mctantwerp.be/)**

  Ten exercises, from variables to a budget tracker. 1 to 6 are for everybody, 7 to 10 are bonus.

  term1.webcoding.learn.mctantwerp.be
- **[02 Code Quest Academy](https://quest.javascript.learn.mctantwerp.be/)**

  Optional. Short challenges with a story, you write and check the code in the browser.

  quest.javascript.learn.mctantwerp.be

The chapters of this deck follow the exercises. Stuck on one? The chapter with the same topic has an example you can run and change.

> **Explanation:** Start with term1 and do the exercises in order, they build on each other. Every exercise has a solution on the site behind View Solution. Use it to compare after you finished, or when you have been stuck for a while, never to start from. The Code Quest is optional: short challenges with a story, checked in the browser. Nice when you finished term1, or when you want to practise in smaller pieces first.

<!-- slide 4 -->

## Agenda

1. Setup and output
2. Input and choices
3. Functions
4. Arrays and loops
5. Objects
6. Timers
7. Exercises

> **Explanation:** Chapters 1 and 2 go with exercises 1, 2 and 6. Chapter 3 with exercise 3. Chapter 4 with exercises 4 and 7, chapter 5 with 5, 8 and 10, and chapter 6 with exercise 9. Every example on these slides runs: press Run, change something, run again. Nothing you break here stays broken, reload the slide and the original code is back.

<!-- slide 5 -->

## 1. Setup and output

> **Explanation:** Every one of the ten exercises asks for the same setup, so get it right once and copy the folder. Half of the 'it does not work' questions in the first weeks are about a script that never loaded, not about the JavaScript inside it.

<!-- slide 6 -->

### Every exercise starts here

#### index.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Exercise 1</title>
</head>
<body>

  <script src="script.js"></script>
</body>
</html>
```

#### script.js

```js
// Only JavaScript in here, no script tags
console.log('script.js is loaded');
```

The script tag goes at the end of the body, so the page is there before the script runs. script.js holds only JavaScript.

> [!TIP]
> One folder per exercise: `ex1/index.html` and `ex1/script.js`.

> **Explanation:** Make a folder webcoding-term1 with a subfolder per exercise: ex1, ex2 and so on. Open the parent folder in VS Code and start Live Server, then browse to ex1/index.html. Open the console straight away (F12, or right click and Inspect): if script.js did not load you see a red 404 there, and it means the name in src does not match the file. The defer attribute in the head would also work, but then document.write does not, and the exercises use document.write.

<!-- slide 7 -->

### Two kinds of output

Run it. The heading and the paragraph land in the page panel, the first line in the console.

```js
const city = 'Antwerp';
const students = 42;

console.log(`Hello from ${city}`);

document.write(`<h2>Hello from ${city}</h2>`);
document.write(`<p>${students} students today</p>`);
```

> [!NOTE]
> `console.log` is for you. `document.write` is for the visitor, and it understands HTML tags.

> **Explanation:** Exercise 1 asks for both, so you will see your text twice: once in the console, once on the page. The console is where you check what your code does while you build it. The page is what a visitor gets. document.write is only for these first exercises: it works while the page loads, and wipes the page when you call it later. From the DOM lesson on, you change a piece of the page instead.

<!-- slide 8 -->

### let and const

Run it, then add the line name = 'Ben' at the end. Which error do you get?

```js
const name = 'Amira';
let age = 19;
const isStudent = true;

age = age + 1;

console.log(typeof name, typeof age, typeof isStudent);
console.log(`${name} is ${age}. Student: ${isStudent}`);
```

> [!TIP]
> `const` unless the value changes, then `let`. `var` never.

> **Explanation:** Take the habit of writing const first. When you get the error 'Assignment to constant variable', you know the value changes and you switch to let. var is how JavaScript did it before 2015, you will still see it in old tutorials and in some AI answers. It works, but has a few surprises let and const fixed, so leave it alone.

<!-- slide 9 -->

### Template literals

Run both presets. Same output, one is easier to read.

```js
const product = 'waffle';
const price = 2.5;
const amount = 3;

console.log(`${amount} x ${product} = €${price * amount}`);
```

*backticks*

```js
const product = 'waffle';
const price = 2.5;
const amount = 3;

console.log(`${amount} x ${product} = €${price * amount}`);
```

*plus*

```js
const product = 'waffle';
const price = 2.5;
const amount = 3;

console.log(amount + ' x ' + product + ' = €' + price * amount);
```

> [!NOTE]
> Backticks, not quotes. Everything between `${` and `}` is JavaScript: a variable, a calculation, a function call.

> **Explanation:** On a Belgian AZERTY keyboard the backtick sits on the key left of Enter, together with the pound sign, and it is a dead key: press it and then a space. You will meet the plus version in older code and in some of the term1 solutions. It gives the same result, but with three variables you lose count of the quotes and the spaces.

<!-- slide 10 -->

### const age = '19'; console.log(age + 1). What shows up?

- A. 20
- B. 191
- C. NaN
- D. An error

**Answer:** B. 191

age is text, because of the quotes. A plus with text on one side glues instead of adds, so you get '191'. 20 would need Number(age) + 1. NaN only shows up when you convert something that is no number at all.

> **Explanation:** This is the bug you will meet most in the exercises, because every answer of prompt() is text, also when somebody types a number. Minus, times and divide do convert text to a number on their own, plus does not. That is why the bug only shows up in some of your calculations.

<!-- slide 11 -->

## 2. Input and choices

> **Explanation:** Real sites never use prompt, they use forms, which come later in the course. But prompt teaches you what every form also needs: the input of a visitor is text, it can be empty or nonsense, and your code has to check it before it calculates with it.

<!-- slide 12 -->

### Text in, number out

Run the three presets. The sandbox answers the prompt for you: 21, then 21 waffles, then Cancel.

```js
const answer = prompt('How many waffles?');

console.log(answer + 1);
console.log(Number(answer) + 1);
```

*prompt() → `21`*

*Number*

```js
const answer = prompt('How many waffles?');

console.log(answer + 1);
console.log(Number(answer) + 1);
```

*prompt() → `21`*

*parseInt*

```js
const answer = prompt('How many waffles?');

console.log(Number(answer));
console.log(parseInt(answer));
```

*prompt() → `21 waffles`*

*cancel*

```js
const answer = prompt('How many waffles?');

console.log(answer);
console.log(answer === null);
```

> [!WARNING]
> `prompt()` always gives back text, or `null` after Cancel. [parseInt on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt)

> **Explanation:** Number() converts the whole string, and gives NaN as soon as one character does not fit. parseInt reads from the start and stops at the first character that is not a digit, so '21 waffles' becomes 21 and '3.7' becomes 3. parseFloat does the same but keeps the decimals. For the exercises Number() is the one to use: it refuses nonsense instead of quietly reading half of it.

<!-- slide 13 -->

### Is it a number

Predict the four lines, then run.

```js
console.log(Number('4'));
console.log(Number('abc'));

console.log(isNaN(Number('4')));
console.log(isNaN(Number('abc')));
```

> [!NOTE]
> `isNaN()` asks: is this Not a Number? `true` means the input is no good. [isNaN on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isNaN)

> **Explanation:** NaN means Not a Number, and it is the result of a conversion that failed. You cannot test it with === NaN, because NaN is not even equal to itself. That is why isNaN exists. Exercises 2, 6 and 9 all ask you to handle invalid input: convert first, then check with isNaN, and show a message instead of calculating.

<!-- slide 14 -->

### The empty answer

```js
const answer = prompt('How many waffles?');
const waffles = Number(answer);
if (answer === null || answer.trim() === '') {
  console.log('No answer');
} else if (isNaN(waffles)) {
  console.log(`${answer} is not a number`);
} else {
  console.log(`${waffles} waffles coming up`);
}
```

*prompt() → ``*

*empty*

```js
const answer = prompt('How many waffles?');
const waffles = Number(answer);
if (answer === null || answer.trim() === '') {
  console.log('No answer');
} else if (isNaN(waffles)) {
  console.log(`${answer} is not a number`);
} else {
  console.log(`${waffles} waffles coming up`);
}
```

*prompt() → ``*

*abc*

```js
const answer = prompt('How many waffles?');
const waffles = Number(answer);
if (answer === null || answer.trim() === '') {
  console.log('No answer');
} else if (isNaN(waffles)) {
  console.log(`${answer} is not a number`);
} else {
  console.log(`${waffles} waffles coming up`);
}
```

*prompt() → `abc`*

*4*

```js
const answer = prompt('How many waffles?');
const waffles = Number(answer);
if (answer === null || answer.trim() === '') {
  console.log('No answer');
} else if (isNaN(waffles)) {
  console.log(`${answer} is not a number`);
} else {
  console.log(`${waffles} waffles coming up`);
}
```

*prompt() → `4`*

> [!WARNING]
> `Number('')` and `Number(null)` are both 0, so `isNaN` lets them through. Check for those first.

> **Explanation:** The order of the checks matters. answer.trim() on null crashes with 'Cannot read properties of null', so the null check comes first, and the || stops as soon as the left side is true. Only when there is an answer, the isNaN check runs. Run the three presets: an empty answer, abc and 4. Exercise 9 needs the null check too: Cancel ends the game.

<!-- slide 15 -->

### Checking text

Run the presets. The sandbox types YES with spaces around it, then an email without @, then Cancel.

```js
const answer = prompt('Sprinkles? yes or no');
const clean = answer.trim().toLowerCase();

console.log(clean);
console.log(clean === 'yes' || clean === 'no');
```

*prompt() → `  YES `*

*yes or no*

```js
const answer = prompt('Sprinkles? yes or no');
const clean = answer.trim().toLowerCase();

console.log(clean);
console.log(clean === 'yes' || clean === 'no');
```

*prompt() → `  YES `*

*includes*

```js
const email = prompt('Your email?');

console.log(email.includes('@'));
console.log(email.length);
```

*prompt() → `amira.student.kdg.be`*

*cancel*

```js
const answer = prompt('Sprinkles? yes or no');
const clean = answer.trim().toLowerCase();

console.log(clean);
```

> [!NOTE]
> `trim()`, `toLowerCase()` and `includes()` give back something new, the original stays the same. [String on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)

> **Explanation:** You can chain them: answer.trim().toLowerCase() first trims, then lowers the result. The cancel preset shows what happens when answer is null: there is no trim on null, so the script stops with an error. includes is case sensitive, so includes('yes') does not find YES. A real email check needs more than an @, but for exercise 6 the @ is enough.

<!-- slide 16 -->

### The first match wins

```js
const battery = 85;
if (battery >= 80) {
  console.log('Full enough');
} else if (battery >= 20) {
  console.log('Fine');
} else {
  console.log('Charge me');
}
```

*right order*

```js
const battery = 85;

if (battery >= 80) {
  console.log('Full enough');
} else if (battery >= 20) {
  console.log('Fine');
} else {
  console.log('Charge me');
}
```

*wrong order*

```js
const battery = 85;

if (battery >= 20) {
  console.log('Fine');
} else if (battery >= 80) {
  console.log('Full enough');
} else {
  console.log('Charge me');
}
```

*and*

```js
const guess = 64;

if (guess >= 1 && guess <= 50) {
  console.log('Valid guess');
} else {
  console.log('Between 1 and 50, please');
}
```

> [!TIP]
> The chain stops at the first condition that is true, so start with the highest limit. In the wrong order preset, 85 says Fine.

> **Explanation:** Exercise 7 is this exact pattern with letter grades: 90 and up is an A, 80 and up a B, and so on. Because the chain stops at the first match, you do not need battery >= 20 && battery < 80: when the code reaches the second check, you already know the battery is below 80. The and preset combines two conditions into one, which you need to check a range like 1 to 50 in exercise 9.

<!-- slide 17 -->

### switch

```js
const size = 'M';
let price;
switch (size) {
  case 'S': price = 2; break;
  case 'M': price = 3; break;
  case 'L': price = 4; break;
  default: price = 0;
}
console.log(`Size ${size} costs €${price}`);
```

> [!WARNING]
> Without a `break` the next case runs too: delete the one after case 'M' and run again. `default` catches everything else.

> **Explanation:** switch is handy when you compare one value with a few fixed options, like the C or F of exercise 2 or the operation of exercise 3. It compares with ===, so 'm' is not 'M': use toUpperCase() on the answer first. For ranges like 'above 80' a switch does not work, that is a job for if and else if.

<!-- slide 18 -->

### What is parseInt('3.7 kg')?

- A. 3.7
- B. 3
- C. NaN
- D. '3.7'

**Answer:** B. 3

parseInt reads digits from the start and stops at the first character that does not fit, the dot. So you get the whole number 3, a number and not a string. Number('3.7 kg') would give NaN because of the kg, and parseFloat('3.7 kg') would give 3.7.

> **Explanation:** When to use which: Number() when the whole answer has to be a number and anything else is an error. parseInt or parseFloat when the number comes with something after it, like a unit, and you only want the number. In the exercises Number() is almost always the right one.

<!-- slide 19 -->

## 3. Functions

> **Explanation:** From exercise 3 on, every exercise asks for functions with a given name, given parameters and a return. That is how bigger programs are built: small pieces that each do one thing and that you can test on their own.

<!-- slide 20 -->

### In and out

```js
function area(width, height) {
  return width * height;
}

const kitchen = area(4, 3);
console.log(kitchen);

console.log(area(2, 5) + area(1, 1));
document.write(`<p>The kitchen is ${kitchen} m²</p>`);
```

> [!NOTE]
> `return` hands the value back to the code that called the function. `console.log` only shows it. [Functions on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions)

> **Explanation:** Run it, then add a line that logs the area of a room of 5 by 6. width and height are parameters, the names inside the function. 4 and 3 are the arguments, the values you pass when you call it. A common mistake is a console.log inside the function instead of a return: the value shows up in the console, but the code that called the function gets undefined and cannot use it. The exercises ask for return on purpose.

<!-- slide 21 -->

### return stops the function

```js
function pricePerPerson(total, people) {
  if (people === 0) {
    return 'Nobody to split the bill with';
  }
  return total / people;
}
console.log(pricePerPerson(60, 4));
console.log(pricePerPerson(60, 0));
console.log(60 / 0);
```

> [!NOTE]
> As soon as a `return` runs, the function is done. Check what can go wrong at the top and return early, the rest never runs.

> **Explanation:** Dividing by zero in JavaScript gives Infinity, not an error, so nothing tells you something went wrong. That is why exercise 3 asks you to check it yourself. A function that returns a number in one case and a sentence in another is fine for an exercise. In a real project you would rather return null or throw an error, but that is for later.

<!-- slide 22 -->

### A function that picks another

```js
function toDollar(euro) { return euro * 1.1; }
function toPound(euro) { return euro * 0.9; }
function convert(euro, currency) {
  switch (currency) {
    case 'USD': return toDollar(euro);
    case 'GBP': return toPound(euro);
    default: return `Unknown currency: ${currency}`;
  }
}
console.log(convert(100, 'USD'), convert(100, 'YEN'));
```

> [!NOTE]
> A `return` inside a case also ends the switch, so no `break` needed.

> **Explanation:** This is the shape of calculate in exercise 3: it takes the operation as a string and calls add, subtract, multiply or divide. With a return in every case you do not need a break, the return leaves the switch and the function at once. The exchange rates are made up. To practise: add a case for CHF with a function of its own.

<!-- slide 23 -->

### function double(n) { n \* 2 }. What does console.log(double(4)) show?

- A. 8
- B. undefined
- C. 4
- D. An error

**Answer:** B. undefined

The function calculates 8 and then throws it away, because there is no return. A function without return gives back undefined, without an error. 4 would mean the function gave back n unchanged, which it does not either. Write return n \* 2.

> **Explanation:** When your function gives undefined, look for the missing return first. The second suspect: a return inside an if, while the other path has none. Every way through the function needs its own return.

<!-- slide 24 -->

## 4. Arrays and loops

> **Explanation:** Nearly all data on a website is a list: products, posts, messages, songs. Arrays hold the list, loops do something with every item. Exercises 4 and 7 are about this chapter, and 8 and 10 combine it with objects.

<!-- slide 25 -->

### An array

```js
const songs = ['Stromae', 'Lorde', 'Bicep'];

console.log(songs[0]);
console.log(songs.length);
console.log(songs[songs.length - 1]);

songs.push('Charlotte de Witte');
console.log(songs);
console.log(songs[10]);
```

> [!NOTE]
> The index starts at 0, so the last item is at `length - 1`. An index that does not exist gives `undefined`, no error. [Array on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)

> **Explanation:** push adds at the end and changes the array itself, also when the array is a const: const only means the variable keeps pointing at the same array. pop takes the last item off, shift and unshift do the same at the start. includes tells you whether an item is in the list. The full list of what an array can do is on MDN.

<!-- slide 26 -->

### Three loops, one list

Run the three presets and compare what they log.

```js
const songs = ['Daft Punk', 'Stromae', 'Lorde'];

for (let i = 0; i < songs.length; i++) {
  console.log(`${i + 1}. ${songs[i]}`);
}
```

*for*

```js
const songs = ['Daft Punk', 'Stromae', 'Lorde'];

for (let i = 0; i < songs.length; i++) {
  console.log(`${i + 1}. ${songs[i]}`);
}
```

*forEach*

```js
const songs = ['Daft Punk', 'Stromae', 'Lorde'];

songs.forEach(function (song) {
  console.log(`♪ ${song}`);
});
```

*while*

```js
const songs = ['Daft Punk', 'Stromae', 'Lorde'];
let count = 0;

while (count < songs.length) {
  count++;
}
console.log(`${count} songs`);
```

> [!TIP]
> `i` is for JavaScript, `i + 1` is for people. [Loops on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration)

> **Explanation:** Exercise 4 asks for all three on the same list, to practise them. In your own code: forEach when you need every item, for when you also need the position or want to skip or stop, while when you do not know how many rounds it will take, like a game that runs until somebody guesses right. Counting with a while is a bit silly when length exists, the exercise only asks for it to practise.

<!-- slide 27 -->

### The index comes along

Run it, then add a fourth player and a fourth score.

```js
const players = ['Amira', 'Ben', 'Chloé'];
const scores = [14, 9, 17];

players.forEach(function (player, index) {
  console.log(`${player}: ${scores[index]}/20`);
});
```

> [!NOTE]
> Two arrays, one index. `players[1]` and `scores[1]` are the same person. [forEach on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)

> **Explanation:** forEach calls your function with up to three arguments: the item, its index and the whole array. You only write the ones you need, and the names are yours to pick. Two arrays that belong together like this are called parallel arrays. They work, but break as soon as somebody sorts one array and not the other. An array of objects keeps a name and its score together, that is the next chapter.

<!-- slide 28 -->

### filter

```js
const cities = ['Gent', 'Antwerp', 'Lier', 'Mechelen'];

const long = cities.filter(function (city) {
  return city.length > 4;
});

console.log(long);
console.log(cities);
```

*filter*

```js
const cities = ['Gent', 'Antwerp', 'Lier', 'Mechelen'];

const long = cities.filter(function (city) {
  return city.length > 4;
});

console.log(long);
console.log(cities);
```

*loop and push*

```js
const cities = ['Gent', 'Antwerp', 'Lier', 'Mechelen'];
const long = [];

for (let i = 0; i < cities.length; i++) {
  if (cities[i].length > 4) {
    long.push(cities[i]);
  }
}
console.log(long);
```

> [!NOTE]
> filter keeps the items for which your function returns `true`. The result is a new array, the original stays the same. [filter on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)

> **Explanation:** Exercise 4 filters on the length of a word, exercise 7 on a grade, exercise 8 on a quantity. The function you give filter has to return true or false. Forget the return and nothing passes, you get an empty array without an error. The second preset does the same job with a loop and push, which is what the solution of exercise 7 does because it has to keep two arrays in sync.

<!-- slide 29 -->

### Adding up

```js
const steps = [8234, 10512, 6120];
let total = 0;
for (let i = 0; i < steps.length; i++) {
  total += steps[i];
}
const average = total / steps.length;
console.log(total);
console.log(average);
console.log(average.toFixed(1));
```

> [!WARNING]
> Start at 0 and add in every round. `toFixed()` gives back text, so round at the end. [toFixed on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed)

> **Explanation:** This pattern is in exercises 5, 7, 8 and 10: a variable at 0 before the loop, and += in every round. The variable has to live outside the loop. Declare it inside and it starts at 0 again every round. toFixed rounds for display and gives back a string, so calculating further with it glues instead of adds. Round at the very end.

<!-- slide 30 -->

### while, until it is done

```js
const secret = 7;
let attempts = 0;
let guess;
while (guess !== secret && attempts < 5) {
  guess = Number(prompt('Guess a number from 1 to 10'));
  attempts++;
}
console.log(`${attempts} attempts, last guess ${guess}`);
```

*prompt() → `3`, `9`, `7`*

> [!WARNING]
> The sandbox answers 3, 9 and 7. Something inside the loop has to change the condition, or it never stops.

> **Explanation:** Change the secret to 5 and run again: now all five attempts are used. A while is the loop for 'keep asking until': you do not know how many rounds it takes. Something inside the loop has to change the condition, here guess and attempts, or it never stops. A real tab then freezes and you have to close it, the sandbox stops it after two seconds. The guessing game of exercise 9 looks like this, but with a pause between the prompts, which a while cannot do. That is the chapter on timers.

<!-- slide 31 -->

### for (let i = 0; i <= names.length; i++) { console.log(names\[i\]) }. names has 3 items. What does the last round log?

- A. The last name
- B. undefined
- C. An error
- D. There is no fourth round

**Answer:** B. undefined

With <= the loop also runs for i = 3, and names\[3\] does not exist. That gives undefined, no error, which is why this bug is easy to miss. With < names.length the loop stops at index 2, the last name.

> **Explanation:** An off by one error, the most common bug in loops, in every language. When the last line of your output says undefined, check the < first. When the first item is missing, check whether i starts at 0.

<!-- slide 32 -->

## 5. Objects

> **Explanation:** An object groups the values that belong to one thing: a student with a name, an age and grades, a product with a price and a stock. Exercise 5 is one object, exercises 8 and 10 are a list of them. That list of objects is the shape almost all data on the web has, so it comes back every lesson from now on.

<!-- slide 33 -->

### An object

```js
const frituur = {
  name: 'Frituur Nummer 1',
  dishes: ['fries', 'frikandel', 'stoofvlees'],
  ratings: { taste: 9, service: 7, price: 8 },
  describe() {
    return `${this.name} serves ${this.dishes.length} dishes`;
  },
};
console.log(frituur.dishes[0], frituur.ratings.taste);
console.log(frituur.describe());
```

> [!NOTE]
> A property can hold an array or another object. A method is a function inside it, `this` is the object.

> **Explanation:** Exercise 5 has exactly this shape: a student with a list of courses (an array) and a grade per course (an object inside the object). Read deeper with a second dot: frituur.ratings.taste. A method is a function that belongs to the object. Inside it, this is the object itself, so this.name is the name of this frituur. Write methods with the short describe() syntax, an arrow function has no this of its own.

<!-- slide 34 -->

### for...in over the keys

```js
const ratings = { taste: 9, service: 7, price: 8 };
let total = 0;
let count = 0;
for (const key in ratings) {
  console.log(`${key}: ${ratings[key]}`);
  total += ratings[key];
  count++;
}
console.log(`Average: ${(total / count).toFixed(1)}`);
```

> [!WARNING]
> The key is in a variable, so read the value with `ratings[key]`, not `ratings.key`. [for...in on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in)

> **Explanation:** for...in gives you the keys, taste, service and price, one by one. ratings.key would look for a property that is literally called key, which does not exist, so you get undefined. To average an object you need to know how many keys it has: count them while you add up, or use Object.keys(ratings).length. This is calculateAverage in exercise 5.

<!-- slide 35 -->

### An array of objects

```js
const tracks = [
  { title: 'Alors on danse', minutes: 3.5, genre: 'pop' },
  { title: 'One More Time', minutes: 5.3, genre: 'house' },
  { title: 'Glue', minutes: 4.4, genre: 'house' },
];
tracks.forEach(function (track) {
  console.log(`${track.title} (${track.minutes} min)`);
});
```

*forEach*

```js
const tracks = [
  { title: 'Alors on danse', minutes: 3.5, genre: 'pop' },
  { title: 'One More Time', minutes: 5.3, genre: 'house' },
  { title: 'Glue', minutes: 4.4, genre: 'house' },
];

tracks.forEach(function (track) {
  console.log(`${track.title} (${track.minutes} min)`);
});
```

*filter*

```js
const tracks = [
  { title: 'Alors on danse', minutes: 3.5, genre: 'pop' },
  { title: 'One More Time', minutes: 5.3, genre: 'house' },
  { title: 'Glue', minutes: 4.4, genre: 'house' },
];

const short = tracks.filter(function (track) {
  return track.minutes < 5;
});
console.log(short);
```

*total*

```js
const tracks = [
  { title: 'Alors on danse', minutes: 3.5, genre: 'pop' },
  { title: 'One More Time', minutes: 5.3, genre: 'house' },
  { title: 'Glue', minutes: 4.4, genre: 'house' },
];

let total = 0;
for (let i = 0; i < tracks.length; i++) {
  total += tracks[i].minutes;
}
console.log(`${total.toFixed(1)} minutes`);
```

*function*

```js
const tracks = [
  { title: 'Alors on danse', minutes: 3.5, genre: 'pop' },
  { title: 'One More Time', minutes: 5.3, genre: 'house' },
  { title: 'Glue', minutes: 4.4, genre: 'house' },
];

function byGenre(list, genre) {
  return list.filter(function (track) {
    return track.genre === genre;
  });
}

console.log(byGenre(tracks, 'house'));
```

> [!NOTE]
> Every item is an object, so you read a property with a dot: `track.minutes`.

> **Explanation:** Run the four presets one by one. This is the shape of exercises 8 and 10. Everything from the arrays chapter works the same, only every item is now an object. The function preset takes the list and a genre as parameters and returns a new list, which is what getProductsByCategory asks. getExpensesByCategory in exercise 10 is a mix: the filter of that preset and the running total of the total preset.

<!-- slide 36 -->

### The highest one

```js
const tracks = [
  { title: 'Alors on danse', minutes: 3.5 },
  { title: 'One More Time', minutes: 5.3 },
  { title: 'Glue', minutes: 4.4 },
];
let longest = tracks[0];
for (let i = 1; i < tracks.length; i++) {
  if (tracks[i].minutes > longest.minutes) { longest = tracks[i]; }
}
console.log(`Longest: ${longest.title}`);
```

> [!NOTE]
> The first item is the winner so far. Every item that beats it takes its place.

> **Explanation:** getHighestExpense in exercise 10 is this. The loop starts at 1 because item 0 is already the winner before the loop. Keep the whole object as the winner, not only its number, so you can show the title or the description afterwards. Flip the > into a < and the same loop finds the lowest one.

<!-- slide 37 -->

### What does filter give back on an array of objects?

- A. A new array with the objects for which the function returned true
- B. true or false
- C. The first object that matches
- D. The same array, with the other objects removed

**Answer:** A. A new array with the objects for which the function returned true

filter always gives back a new array, also when only one object matches (an array with one item) or none at all (an empty array). The original array stays exactly as it was. The first match on its own is what find gives you.

> **Explanation:** A typical mistake after a filter: you expect one product and write result.name, but result is an array, so you get undefined. Use result[0].name, or find instead of filter when you only want one.

<!-- slide 38 -->

## 6. Timers

> **Explanation:** Only exercise 9 needs this chapter, and it is the hardest of the ten. A game that waits a moment between two questions cannot be written with a normal loop. The trick on these slides comes back later in the course, every time something has to happen again with a pause in between.

<!-- slide 39 -->

### setTimeout

Predict the order of the three lines, then run.

```js
function sayLater() {
  console.log('One second later');
}

console.log('First');
setTimeout(sayLater, 1000);
console.log('Second');
```

> [!WARNING]
> `setTimeout(sayLater, 1000)`, not `setTimeout(sayLater(), 1000)`. [setTimeout on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout)

> **Explanation:** setTimeout does not pause your script. It puts the function aside and moves on to the next line, which is why Second shows up before One second later. The second argument is the delay in milliseconds, 1000 is one second. With brackets, setTimeout(sayLater(), 1000), the function runs right away and setTimeout gets its result, undefined, instead of the function.

<!-- slide 40 -->

### A loop with a pause

```js
let count = 3;
function tick() {
  console.log(count);
  count--;
  if (count > 0) {
    setTimeout(tick, 1000);
  }
}
tick();
```

> [!NOTE]
> `tick` plans its own next round with `setTimeout`. The `if` is the brake: without it, the countdown never stops.

> **Explanation:** A for or a while runs all its rounds in one go, the browser gets no chance to wait in between. So the function plans its own next round with setTimeout. A function that calls itself is called recursion. For exercise 9: playGame asks for a guess, shows too high or too low, and calls setTimeout(playGame, 1000) for the next attempt. It stops when the guess is right, after five attempts, or when prompt gives null.

<!-- slide 41 -->

### document.write after a timer

Run it and watch the heading in the page panel.

```js
document.write('<h2>Guess the number</h2>');

setTimeout(function () {
  document.write('<p>Too high!</p>');
}, 1000);
```

> [!WARNING]
> After a timer the page has loaded, so `document.write` starts a new, empty page.

> **Explanation:** Exercise 9 asks for document.write, and the solution on the site uses it after setTimeout. It works, but the first write after the timer starts a new page, so the title and the intro of the game disappear after the first guess. The writes after that add to the new page. For the exercise that is acceptable. If it bothers you, show the feedback with alert or console.log. The real fix comes with the DOM.

<!-- slide 42 -->

### Which one can wait a second between two prompts?

- A. A for loop
- B. A while loop
- C. A function that calls itself with setTimeout
- D. forEach

**Answer:** C. A function that calls itself with setTimeout

Loops run all their rounds in one go, the browser gets no chance to wait in between. A while with a prompt in it does ask again and again, but without a pause and without time to show the feedback. setTimeout puts the next round aside, so the page gets a second first.

> **Explanation:** If you put a setTimeout inside a for loop, all timers are set at once, in the same millisecond, and they all go off together a second later. That is not a pause between rounds. The recursive function is the way.

<!-- slide 43 -->

## 7. Exercises

> **Explanation:** Do them in order. Exercise 1 takes five minutes, but it is also where your folder and your setup get tested. Keep this deck open next to the exercises, the chapters follow the same order.

<!-- slide 44 -->

### How it works

- Exercises 1 to 6 are for everybody, **7 to 10 are bonus**. They go from easy to hard.
- One folder per exercise, an `index.html` and a `script.js`, opened with Live Server and the console open.
- Every exercise has a solution on the site. It is one way to solve it, not the only one: if your code works and you can explain every line, it is right.
- Stuck for more than ten minutes? Read the error in the console, look up the chapter in this deck, ask a classmate or on Discord. Then look at the solution.

> **Explanation:** Looking at a solution after you tried is fine and you learn from it: compare it with yours and find what you did differently. Looking before you tried is the same as asking AI for the whole thing, it feels like progress and it is not. Questions go to the #help-needed forum on Discord, with a screenshot of your code and the error.

<!-- slide 45 -->

### AI, for questions

#### Ask this

How do I convert a string to a number in JavaScript?

What is the difference between `forEach` and `map`?

How can I check if an email contains an @?

What does `isNaN()` do?

#### Not this

Can you solve exercise 3 for me?

Write the complete code for this exercise.

**[AI as a student](https://slides.mctantwerp.be/en/general/09-ai-as-a-student)**: Where a language model moves you forward, where it wrecks your learning, and what you have to say about it.

> **Explanation:** This is the rule from the exercise site. A specific question gets you unstuck, and you still write and understand the code yourself. A whole solution gets you a finished exercise and an exam in January you cannot do: on a laptop, with the same kind of exercises, and without the AI. The deck on the right explains what is allowed and how to say you used AI.

<!-- slide 46 -->

### When it does not work

| What you see | Why |
| --- | --- |
| `GET .../script.js 404` | The name in src does not match the file. |
| `x is not defined` | A typo in a name, or the variable lives inside another function. |
| 23 instead of 5 | Text from `prompt`, `Number()` is missing. |
| `NaN` | A calculation with text that is no number, or with `undefined`. |
| `undefined` from your function | The `return` is missing. |
| `Cannot read properties of null` | Cancel was clicked, and the next line calls a method on `null`. |

> **Explanation:** The console tells you the file and the line number next to every red error, click it and the browser shows you the line. Read the message itself before you change anything: 'is not defined' and 'Cannot read properties of null' point at very different problems. When nothing is red and the result is still wrong, add console.log lines to see what your variables hold halfway.

<!-- slide 47 -->

### Look it up

- **[Hello World](https://slides.mctantwerp.be/en/webcoding/02-hello-world)**: Variables, types, converting, if and switch
- **[Programming in JS](https://slides.mctantwerp.be/en/webcoding/03-programming-in-js)**: Functions, arrays, loops, objects and prompt
- **[JavaScript & HTML](https://slides.mctantwerp.be/en/webcoding/04-javascript-and-html)**: script.js, the window object, timers and the first click
- **[Cheat sheet](https://term1.webcoding.learn.mctantwerp.be/cheat-sheet.html)**: All of period 1 on one long page, on the exercise site.

> **Explanation:** Everything in this deck was explained at more length in one of these three lessons, with more examples and exercises with solutions of their own. The cheat sheet on the exercise site has the syntax of all of it on one page, handy to keep open in a second tab. Keep it for the exam too, which is open book.

<!-- slide 48 -->

### Task: Term 1, exercises 1 to 6

[term1.webcoding.learn.mctantwerp.be](https://term1.webcoding.learn.mctantwerp.be/). Done when every exercise runs without a red line in the console and you can explain every line.

1. Variables and output: console.log and document.write
2. Temperature converter: prompt, Number and if/else
3. Calculator functions: return, switch and dividing by zero
4. Shopping list: for, forEach, while and filter
5. Student information: a nested object and for...in
6. User profile: validating every prompt

> **Explanation:** Where to look when stuck: exercises 1 and 2 in chapters 1 and 2, exercise 3 on 'A function that picks another' and 'return stops the function', exercise 4 on 'Three loops, one list' and 'filter', exercise 5 on 'An object' and 'for...in over the keys', exercise 6 on 'The empty answer' and 'Checking text'. Start every exercise by writing the steps of the assignment as comments in script.js, then fill in the code under each comment.

<!-- slide 49 -->

### Task: Bonus, exercises 7 to 10

For whoever finished 1 to 6. Done when the output matches the assignment, also for wrong input and for Cancel.

1. Grade calculator: two arrays, letter grades and an average
2. Product inventory: an array of objects and functions on it
3. Number guessing game: Math.random, setTimeout and Cancel
4. Personal budget tracker: totals per category and the highest expense

> **Explanation:** Where to look when stuck: exercise 7 on 'The first match wins', 'The index comes along' and 'Adding up'. Exercise 8 on 'An array of objects'. Exercise 9 on 'The empty answer' and the whole timers chapter, start from 'A loop with a pause'. Exercise 10 on 'An array of objects' and 'The highest one'. Exercise 9 is the hardest of the ten: get the game working with one attempt first, then add the timer and the limit of five.

<!-- slide 50 -->

### Code Quest Academy

| Quest | What is in it | Ready? |
| --- | --- | --- |
| 1. The Script Awakens | console.log, variables, operators, if, switch, loops | Yes |
| 2. The Logic Trials | Conditions, functions, arrays, objects, for...of | Yes |
| 3. The Browser Frontier | window, location, navigator, timers, console.warn | After the DOM lesson |
| 4 to 7 | The DOM, validation, libraries and jQuery | Period 2 |

Optional, for after term 1 or between two exercises: [quest.javascript.learn.mctantwerp.be](https://quest.javascript.learn.mctantwerp.be/)

> **Explanation:** The quest checks your code in the browser and keeps your progress in that browser, so use the same laptop and browser to continue later. Every quest follows a lesson of this course, in the form of a story. Quest 3 also writes into the page with a few lines of DOM, which is the next lesson, so it is fine to leave that one for later.

<!-- slide 51 -->

### Next up

- **22/10** Practice session: HO-A.01.36
- **29/10** DOM: The most important lesson of them all

> **Explanation:** The DOM lesson assumes the basics of this deck are there: variables, functions, arrays, objects and loops. So finish exercises 1 to 6 before then. From the DOM on, JavaScript changes a piece of the page instead of writing a new one, and prompt and document.write can retire.

## Glossary

- **isNaN()**: Gives back true when a value is not a number after conversion. isNaN(Number(answer)) catches text like abc, but not an empty answer: Number('') is 0. (slide 13)
- **parseInt()**: Reads a whole number from the start of a string and stops at the first character that does not fit: parseInt('21 waffles') is 21. Number() gives NaN there. (slide 12)
- **toFixed()**: Rounds a number to a fixed number of decimals and gives back a string: (8288.666).toFixed(1) is '8288.7'. For showing, not for calculating further. (slide 29)
- **String methods**: Functions every piece of text has: trim() drops the spaces around it, toLowerCase() makes it lower case, includes('@') checks whether it contains something. They give back something new, the original stays the same. (slide 15)
- **Parallel arrays**: Two arrays where the same index belongs to the same thing: names\[2\] and scores\[2\] are one person. Works, but an array of objects keeps them together. (slide 27)
- **Running total**: A variable that starts at 0 before a loop and grows with += in every round. After the loop it holds the sum, and the sum divided by the length is the average. (slide 29)
- **Recursion**: A function that calls itself. With setTimeout in between, every round waits before the next one starts, which a for or a while cannot do. It needs a condition that stops it. (slide 40)

## Links and sources

### In the deck

- [Webcoding term 1: exercises](https://term1.webcoding.learn.mctantwerp.be/) (slide 3, slide 48): Ten exercises with their solutions, 7 to 10 are bonus
- [Code Quest Academy](https://quest.javascript.learn.mctantwerp.be/) (slide 3, slide 50): Optional, story driven challenges in the browser
- [parseInt on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt) (slide 12)
- [isNaN on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isNaN) (slide 13)
- [String on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) (slide 15)
- [JavaScript Guide: Functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions) (slide 20)
- [Array on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) (slide 25)
- [JavaScript Guide: Loops and iteration](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration) (slide 26)
- [forEach on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) (slide 27)
- [filter on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) (slide 28)
- [toFixed on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) (slide 29)
- [for...in on MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in) (slide 34)
- [setTimeout on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout) (slide 39)
- [Webcoding term 1: cheat sheet](https://term1.webcoding.learn.mctantwerp.be/cheat-sheet.html) (slide 47)
- [kdgmt.be/discord](https://kdgmt.be/discord) (slide 52)
