Vocabulary for this course

The terms the chapters assume, in plain words. You can open this from any chapter.

addEventListener
A call on an element that registers a listener for one kind of event.
closest
A way to find the nearest parent of an element that matches a rule you give, starting with the element itself.
closure
A function together with the variables from the place it was written, which stay available when it runs later.
default behaviour
What the browser does on its own when an event happens, such as following a link or submitting a form, before any script intervenes.
DOM
The tree of objects a browser builds from the page, which a script can read and change while the page is open.
element
One item in the page tree, such as a heading or a button, which a script can find and change.
event
Something that happens on the page, such as a click, a key press, or a form being submitted, which a script can respond to.
event bubbling
The way an event rises from the element that received it up through each of its parents.
event target
The element that actually received the action, which may be a child of the element whose listener is running.
function
A reusable block of code that can be called with values and can return an answer to whoever called it.
handler
The function you give to a listener, which the browser runs when the event it waits for happens.
listener
A function registered on an element to wait for one kind of event and run each time that event reaches it.
property
A named value stored on an object, usually read and written with a dot, such as the text of an element.
reference
A value that points at an object rather than holding a copy, so a change made through one is visible through the other.
scope
The part of a program where a name can be used, decided by where it was declared.
variable
A name that holds a value so you can use it again; a constant cannot be pointed at a different value later.

JavaScript from the ground up

Decisions and loops

Asking questions with if, repeating work with for, and walking a list with the for…of shape.

Part 3 of 12

Beginner14 min readBack to Courses

if, and the strictness of ===

An if runs the block under it when the question is true; else catches the rest. The braces mark the block, so the layout is free — but indenting the inside is what makes it readable.

JavaScript has two equality operators, and the difference matters. === asks whether two values are the same type and value; == will convert before comparing, so "3" == 3 is true and "3" === 3 is not. Use === — it asks the question you meant.

The extra equals sign is not decoration. == has a table of conversions, and the table has cases that surprise even experienced programmers: null == undefined is true, and 0 == "" is true. === has no table, so the answer depends only on the two values in front of you.

A question, and a walk through a list

const temperature = 12;

if (temperature > 18) {
  console.log("Light jacket.");
} else {
  console.log("Warm coat.");
}

const loaves = ["white", "rye", "focaccia"];

for (const loaf of loaves) {
  console.log(loaf);
}

for…of, for arrays

for (const loaf of loaves) hands the body one item per turn, in order — the same shape as for item in list in Python. The name inside is just a name for the current item; it is not an index and it has nothing to do with the array’s positions.

The older for (let i = 0; i < loaves.length; i++) still exists, and is what you reach for when the position genuinely matters. When it does not, the for…of form says what is happening with less to read.

Combining questions

&& means both, || means either, and ! flips a boolean. They read as words if you say them that way: "if there is flour and there is time, bake".

Keep one question per if where you can. A condition with three &&s is usually two decisions wearing a coat, and the second one deserves its own block.

What counts as true

An if does not demand a boolean. It accepts any value and asks whether it is truthy. Six values are falsy: false, 0, "", null, undefined and NaN. Everything else is truthy — including "0", "false" and an empty array.

That shortcut is convenient and occasionally wrong. if (input.value) looks like "if something was typed", and it is — but a field containing a space is truthy, and a field containing the text "0" is truthy too. When zero is a legal answer, test the value explicitly instead of relying on truthiness.

Falsy is a short list, and it is worth memorising because the exceptions are where bugs live: 0, "", null, undefined, NaN and false. Everything else is truthy, including an empty array and an empty object, which is why if (list) does not test whether the list has items.

The three parts of a counting loop

A counting loop has a start, a test and a step: for (let i = 0; i < 5; i++). The test runs before every turn, including the first, so i < 5 gives five turns numbered 0 to 4. Writing i <= 5 gives six, which is the off-by-one mistake in its natural habitat.

The step is what guarantees the loop can end. Remove i++ and the test stays true forever; the page freezes and the browser eventually offers to stop the script. If a loop misbehaves, log the counter on the first few turns — the answer is usually visible immediately.

A worked loop, traced

Take for (let i = 0; i < 5; i++). Before the first turn i is 0, the test passes, and the body runs with 0. The step adds one, and the same three steps repeat. When i reaches 5 the test fails, the body is skipped, and the loop is over. The values seen inside are 0, 1, 2, 3 and 4 — five turns, none of them 5.

Change the test to i <= 5 and the body also runs with 5, which is six turns. Change the step to i += 2 and the values are 0, 2, 4, then 6, which fails the test; three turns. Read the start, the test and the step together, and the count follows.

Three mistakes to recognise: if (x = 5) assigns and is always true, so use === for the comparison; a counting loop with no step never ends; and an empty string is falsy while the string "0" is truthy, so a condition built on a field value needs to say what it actually means.

Try these next

  • Count down from 5 to 1 with a loop, then log "liftoff" after it.
  • Log only the even numbers from 1 to 10, using continue to skip the odd ones.
  • Rewrite a counting loop as for…of over an array, and notice which name disappears.

Odd or even

Walk the numbers from 1 to 6 with a loop, and log each one with "odd" or "even" after it.

Live Preview
Console output will appear here...

3 left to do

  • The numbers are walked with a loop
  • There is a decision in the loop
  • Each result is logged

for (let n = 1; n <= 6; n++) counts; n % 2 === 0 is the even test (the remainder of dividing by two).

A loop that adds up

Add the whole numbers from 1 to 10 with a loop, keep the running total in a variable, and log the answer. Then log the even numbers from 1 to 10 on their own.

Live Preview
Console output will appear here...

3 left to do

  • The numbers are walked with a loop
  • A running total is kept
  • The answer is logged

Start let total = 0;, then total += n; inside the loop. For the evens, if (n % 2 === 0) decides whether to log.

A loop that logs nothing usually means the list is empty or the condition is never true. Log the list itself once — nine times out of ten that is the whole bug.

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

Why prefer === over ==?