Controlling the flow
As mentioned earlier, code is executed line-by-line, from top to bottom. This is pretty limiting - we normally want to run code differently depending on what is going on. For example, to execute that chunk of code if this is happening, or run that other chunk of code if something else is happening. Or perhaps we want to run a chunk of code several times - it would be silly to copy and paste the lines of code repeatedly.
Some concrete examples
- If its the second time the person has clicked a button then do this instead of that
- If the touch point is in the top half of the screen do this, but otherwise don't do anything.
- If the shift key is bring pressed while dragging trigger this
These types of expressions are logical expressions, meaning they can be evaluated to either true or false (boolean). Not everything boils down to true or false, keep in mind. For example you can't active a light based on the expression "What is the temperature?" - because the response is not a true or false value, it's a numeric value, or if you're asking a human it may be something qualitative - "it feels pretty toasty".
In those cases, you can maybe get to something true or false with some logical operators. For example:
- Is the temperature below 0?
- Is the temperature above 15 and below 25?
- Is the temperature an even number?
- Does the person use the word 'toasty' to describe the temperature?
Logical operators to use for these kinds of expressions are:
>
greater than;>=
greater than or equal to<
less than;<=
less than or equal to===
equals to!==
not equal to
If you are working with a boolean, it already is true or false, so you can use it as-is:
if (evt.shiftKey) {
// Shift key is held
}
Booleans can also use the !
operator which flips the value:
if (!evt.shiftKeys) {
// Shift key is not held
}
Otherwise we could use a logical operator to get to some kind of true/false result:
if (numberOfClicks === 2) {
// do this
} else {
// do that
}
if (names.contains('bob')) {
}
if (evt.y < 400) {
// do this
}
Logical expressions can be complicated if you need them to be. Use the and double ampersand &&
: "If the sky is blue and the day is Monday and I am hungry then buy ice cream, otherwise buy a beer".
if (skyColour === "blue" && day === "Monday" && hungry) {
// buy ice cream
} else {
// buy a beer
}
You can also express an or relation using the double pipe ||
: "If the sky is blue or it is Monday, then buy an ice cream"
if (skyColour === "blue" || day === "Monday") {
// buy an ice cream
}
If you want to execute something based on it being false rather than true, use the bang (!
):
if (skyColour !== "blue") {
// buy ice cream if the sky is not blue
}
if (!hungry) {
// have a nap when not hungry
}
Many other kinds of expressions are possible, but it must be something that boils down to true or false.
You can combine logical expressions:
if ((age > 20 && age < 30) || (eyeColour === "green")) {
// Age is between 20 and 30 or eye colour is green
}
You can also evaluate multiple different expressions:
if (age > 50) {
// buy aquavit
} else if (age > 30) {
// buy wine
} else if (age > 20) {
// buy gin
} else if (age > 16) {
// buy beer
} else {
// buy a juice
}
Note
You may also see ==
and !=
used instead of ===
and !==
. There is a very subtle difference between the two styles. The triple-equals form (===
and !==
) checks if values are equal without trying to convert the values to the same type. The double-equals form (==
and !=
) compares values after they have been converted to the same type. This can lead to unexpected behaviour.
For example, false == '0'
is true, while false === '0'
is false. 0 == '0'
is true (even though one variable is a number and one is a string), while 0 === '0'
is false because of the type mismatch.
When in doubt, use the triple-equals format.
Switches
A common pattern, as illustrated in the previous large if-else-else-if example is taking a course of action depending on the value of a variable. A 'switch' is commonly used for this. The general form is:
switch (expression) {
case value1:
// code
break;
case value2:
// code
break;
default:
// run this if value1 or value2 weren't found
}
Where expression
is an expression (but commonly just a variable), and "value1" and "value2" represent the values you want to check. You can have unlimited of these. It's important to add break
at the end, otherwise execution continues running the subsequent code blocks even though they do not match the expression. The default
block is for handling values which don't match anything else.
Example:
switch (dayOfWeek) {
case "Monday":
console.log("Oh no, another start of the week");
break;
case "Friday":
console.log("Yay! end of the week!");
break;
default:
console.log("Boring " + dayOfWeek);
}
Read more
- switch statement (MDN)
Loops
Loops are control statements which repeat a section of code under a set of conditions. A common loop is the for loop, which is typically used when you want to continue incrementally until you reach a certain threshold.
The general form is:
for (initialiser; condition; mutator) {
// code to run
}
For example, here we initialise a counter named i
, start it at 0
, continue the loop for as long as i
is less than 10, and we increment by one each time:
for (let i=0; i<10; i++) {
console.log(i);
}
This will print 0, 1, 2 .. all the way to 9. 10 is not printed because it only loops while i
is less than 10.
Very commonly, you'll see this pattern being used for looping through every item in an array:
for (let i=0; i<names.length; i++) {
console.log(names[i]); // Prints out each name
}
If there's no counting or ranges involved, a while loop might be what you want. It will continue looping for as long as an expression is true. The basic form is:
while (expression) {
// do this
}
For example the following loop will run and continue to loop until the sky is no longer "blue". You have to be careful that loops don't run forever. You want to make sure that at some point the expression could become false, otherwise the loop will never end.
while (sky == "blue") {
// hum a song
}
Skipping
In loops you can use the keyword continue
to jump back to the start point of the loop. In the following example, the console.log
line is skipped for some names.
for (let name of names) {
// Skip names containing 'Clint'
if (name.indexOf("Clint") >= 0) continue;
// Print out the name
console.log(name);
}
Bailing out
Another aspect of controlling flow is exiting. break
is how one can exit out of a loop or switch block.
In the below example, even though the loop should run forever (because true is always true), the addition of the break
statement means it will exit after the first run, and execution will continue after the while scope
while (true) {
console.log("Hello");
break;
}
// execution continues here