- The global scope
- Blocks and local scope
- If, else
- While, break, continue
- For ... of ...
- For ... in ...
- Bounded loops
- Block labels
So far, we’ve mostly written lines of code that run one after the other.
But when we code, we can control the order of execution of it.
This is called control flow.
Let’s start with understanding blocks and scope.
The global scope
Every variable we declare exists in a scope, which means the region of the code where the variable is known.
If you declare a variable outside of any block, it exists in the global scope.
const color = "blue" console.log(color)
This variable
color is in the global scope, so it can be accessed from anywhere in the file.If you add more lines:
const color = "blue" console.log(color) const size = "large" console.log(color) console.log(size)
Both
color and size are global variables. They are available everywhere in the file.But what happens inside a block?
Blocks and local scope
A block is a piece of code surrounded by curly braces
{}.Variables declared with
let or const inside a block exist only inside that block.{ const message = "inside block" console.log(message) }
This prints:
inside block
But if you try this:
{ const message = "inside block" } console.log(message) // Error!
JavaScript will give you an error like:
ReferenceError: message is not defined
That’s because
message was declared inside the block and doesn’t exist outside of it.This means we can use blocks to isolate portions of our code, and be sure that "what happens in the block stays in the block" (kinda like Las Vegas).
Organizing our code in blocks allow us also to structure the execution of the program, with control flow constructs like
ifif, else
Sometimes we want to run code only if something is true. That’s what the
if statement is for.const myAge = 20 console.log("Am I an adult?") if (myAge >= 18) { console.log("Yes I am!") }
This prints:
Am I an adult? Yes I am!
As you can, see the code compares
myAge and 18.
In this case the >= operator returns true, so the block gets executed.
If the condition is not true, the block doesn't get executed.const myAge = 17 console.log("Am I an adult?") if (myAge >= 18) { console.log("Yes I am!") }
This prints:
Am I an adult?
You can add an
else block to handle the opposite case:const myAge = 17 console.log("Am I an adult?") if (myAge >= 18) { console.log("Yes I am!") } else { console.log("No, I am not.") }
This prints:
Am I an adult? No, I am not.
Both the
if and else blocks are still blocks - so variables declared inside them don’t exist outside.If we want to be sure that something is not true, what can we do?
Well, as previously discussed, JavaScript has a "not" operator, which flips booleans. So we can do
const myAge = 17 const adult = myAge >= 18 console.log("Am I an adult?") if (!adult) { console.log("No, I am not.") }
This still prints:
Am I an adult? No, I am not.
Because we used the
! operator to invert the adult variable.if (!adult) {...} should be read as "if not adult..."Using blocks, logic and comparison operators, we can structure the execution of the program, by defining variables that must be
true (or false) for something to happen.while, break, continue
A
while loop repeats code as long as a condition is true.let count = 0 while (count < 3) { console.log("Count is", count) count = count + 1 } console.log("the loop is over!")
This prints:
Count is 0 Count is 1 Count is 2 the loop is over!
When
count becomes 3, the loop stops.You can stop a loop early using
break:let number = 1 // Start with number 1 while (true) { // This condition is always true, so this loop will run forever unless we stop it console.log(number) // Print the current number if (number === 3) { // If the number is 3, stop the loop break } number = number + 1 // Add 1 to the number }
This prints:
0 1 2
Because when the number becomes
3, the if block gets executed and it stops the loop.You can skip the rest of a loop using
continue:let number = 0 // Start with number 0 while (number < 5) { // Keep going while number is less than 5 number = number + 1 // Add 1 to the number if (number === 3) { // If the number is 3 continue // Skip the rest of the block and go to the next iteration of the loop } console.log(number) // Print the number }
This prints:
1 2 4 5
Because when the number was
3, continue made the program skip the line that prints the number.for ... of ...
If you have an array, and want to do something to every item in it, you can use
for ... of ... {...}.const fruits = ["apple", "banana", "cherry"] for (const fruit of fruits) { console.log(fruit) }
This prints:
apple banana cherry
The block will get executed once for each element of the array.
fruit here is a new variable that takes the value of each item in the array, to operate on it inside the block.for ... in ...
You can use
for ... in to loop over the keys (indexes) of an array:const fruits = ["apple", "banana", "cherry"] for (const index in fruits) { console.log(index) }
This prints:
0 1 2
You can use the index to get the value too:
const fruits = ["apple", "banana", "cherry"] for (const index in fruits) { console.log(fruits[index]) }
This prints the same as
for ... of:apple banana cherry
In practice, for arrays, you should prefer using
for ... of, as it's simpler and cleaner.Bounded loops
Sometimes we want to loop a specific number of times, or in general write a piece of code that repeats a block while keeping track of something.
That’s what a bounded
for loop is good for.
A bounded loop usually takes three conditions, separated by a semicolon ;, as in (... ; ... ; ....).for (let i = 0; i < 3; i = i + 1) { console.log(i) }
This prints:
0 1 2
Let’s explain it:
let i = 0: declares a variable to be used in the block (in this case it's a counter that starts at 0)i < 3: declares a condition to betruefor the block to be executed ( in this case is "repeat whileiis less than 3")i = i + 1: declare some code to be run after each execution of the block (in this case "increaseiby 1")
As you can see the bounded loop us to declare more complex conditions for the repeated execution of a piece of code, but most of the times it's not necessary.
Block labels
If you have to write more complex control flow, JavaScript lets you name a block using a label that can be used by
break or continue for specifying where to jump back.Example:
outer: { console.log("We're inside the outer scope.") inner: { console.log("We're inside the inner scope.") break outer } console.log("This will not run") } console.log("Done")
This prints:
We're inside the outer scope We're inside the inner scope. Done
We used
break outer to exit the outer block entirely.You can also label loops. Let's take this example:
// Declare a variable to count the total number of days in a year let totalDaysInOneYear = 0 // Declare one variable per month, with the number of the month const january = 1 const february = 2 const march = 3 const april = 4 const may = 5 const june = 6 const july = 7 const august = 8 const september = 9 const october = 10 const november = 11 const december = 12 // Declare an array that holds the months that have 30 days const monthsWith30Days = [ april, june, september, november ] // Declare variables to keep track of the month and day we're in let currentMonth = january let currentDay = 1 monthsLoop: while (true) { // Start a loop labeled "monthsLoop" to process each month daysLoop: while (true) { // Start a loop labeled "daysLoop" to process each day in the month totalDaysInOneYear = totalDaysInOneYear + 1 // Increase the total number of days we counted by 1 if ( // We want to check if we're at the end of the month. currentDay === 31 // Check if the current day is 31 (for months with 31 days)... || currentDay === 30 && (monthsWith30Days.includes(currentMonth)) // ...or 30 if it's among the 30-days months... || currentDay === 28 && (currentMonth === february) // ...or 28 if it's February. If it's any of these three, then: ){ currentMonth = currentMonth + 1 // Move to the next month currentDay = 1 // Reset the day to 1 for the new month break daysLoop // Exit the inner loop (which tracks days) and go back to the outer loop (which tracks months) } else { currentDay = currentDay + 1 } // Otherwise, we're not at the end of the month, and we just move to the next day } if (currentMonth > 12) { // After processing a month, check if we've gone past December break monthsLoop // If so, break the outer loop and stop the day-counting process } } console.log(totalDaysInOneYear) // Print the total number of days in the year (should be 365)
This was a very boring example but hopefully it clarified the (occasional) need for labels.