[flaviocopes] The JavaScript Fundamentals Course - JavaScript Control Structures

Introduction

Control structures determine how the program flows.

We have 2 important types of control structures.

The first is conditionals, and the second is loops. All of the things described in this section are statements .

JavaScript has different kind of structures that can help us in all the different situations:

Conditionals

  • if / else
  • switch

Loops

  • for
  • for…of
  • for…in
  • while
  • do…while
  • Array.forEach

Let’s go into each single one.

If / else

An if statement is used to make the program take a route, or another, depending on the result of an expression evaluation.

This is the simplest example, which always executes:

if (true) {
  //do something
}

on the contrary, this is never executed:

if (false) {
  //do something (? never ?)
}

If you have a single statement to execute after the conditionals, you can omit the block, and just write the statement:

if (true) doSomething()

The conditional checks the expression you pass to it for true or false value. If you pass a number, that always evaluates to true unless it’s 0. If you pass a string, it always evaluates to true unless it’s an empty string. Those are general rules of casting types to a boolean.

Else

You can provide a second part to the if statement: else .

You attach a statement that is going to be executed if the if condition is false:

if (true) {
  //do something
} else {
  //do something else
}

Since else accepts a statement, you can nest another if/else statement inside it:

if (a === true) {
  //do something
} else if (b === true) {
  //do something else
} else {
  //fallback
}

Switch

An if/else statement is great when you have a few options to choose.

When they are too many however it might be overkill. Your code will look too complex.

In this case you might want to use a switch conditional:

switch(<expression>) {
  //cases
}

based on the result of the expression, JavaScript will trigger one specific case you define:

const a = 2
switch(a) {
  case 1:
    //handle case a is 1
    break
  case 2:
    //handle case a is 2
    break
  case 3:
    //handle case a is 3
    break
}

You must add a break statement at the bottom of each case, otherwise JavaScript will also execute the code in the next case (and sometimes this is useful, but beware of bugs). When used inside a function, if the switch defines a return value, instead of using break you can just use return :

const doSomething = (a) => {
  switch(a) {
    case 1:
      //handle case a is 1
      return 'handled 1'
    case 2:
      //handle case a is 2
      return 'handled 2'
    case 3:
      //handle case a is 3
      return 'handled 3'
  }
}

You can provide a default special case, which is called when no case handles the result of the expression:

const a = 2
switch(a) {
  case 1:
    //handle case a is 1
    break
  case 2:
    //handle case a is 2
    break
  case 3:
    //handle case a is 3
    break
  default:
    //handle all other cases
    break
}

Loops

JavaScript provides many way to iterate through loops. This lesson explains each one with a small example and the main properties.

for

const list = ['a', 'b', 'c']
for (let i = 0; i < list.length; i++) {
  console.log(list[i]) //value
  console.log(i) //index
}
  • You can interrupt a for loop using break
  • You can fast forward to the next iteration of a for loop using continue

do...while

const list = ['a', 'b', 'c']
let i = 0
do {
  console.log(list[i]) //value
  console.log(i) //index
  i = i + 1
} while (i < list.length)

You can interrupt a while loop using break :

do {
  if (something) break
} while (true)

and you can jump to the next iteration using continue :

do {
  if (something) continue

  //do something else
} while (true)

while

const list = ['a', 'b', 'c']
let i = 0
while (i < list.length) {
  console.log(list[i]) //value
  console.log(i) //index
  i = i + 1
}

You can interrupt a while loop using break :

while (true) {
  if (something) break
}

and you can jump to the next iteration using continue :

while (true) {
  if (something) continue

  //do something else
}

The difference with do...while is that do...while always execute its cycle at least once.

for...in

Iterates all the enumerable properties of an object, giving the property names.

for (let property in object) {
  console.log(property) //property name
  console.log(object[property]) //property value
}

for...of

ES2015 introduced the for...of loop, which combines the conciseness of forEach with the ability to break:

//iterate over the value
for (const value of ['a', 'b', 'c']) {
  console.log(value) //value
}

How can you get the index of an iteration?

The loop does not offer any syntax to do this, but you can combine the destructuring syntax introduced in ES2015 with calling the entries() method on the array:

//get the index as well, using `entries()`
for (const [index, value] of ['a', 'b', 'c'].entries()) {
  console.log(index) //index
  console.log(value) //value
}

Notice the use of const . This loop creates a new scope in every iteration, so we can safely use that instead of let .

The difference with for...in is:

  • for...of iterates over the property values
  • for...in iterates the property names

Array.forEach

Introduced in ES5. Given an array, you can iterate over its properties using list.forEach() :

const list = ['a', 'b', 'c']
list.forEach((item, index) => {
  console.log(item) //value
  console.log(index) //index
})

//index is optional
list.forEach(item => console.log(item))

unfortunately you cannot break out of this loop.

Scope in loops

We talked about scope previously. There is one interesting thing about scope and loops that might cause a few headaches to developers, so let’s explain it here.

Take this example:

const operations = []

for (var i = 0; i < 5; i++) {
  operations.push(() => {
    console.log(i)
  })
}

for (const operation of operations) {
  operation()
}

It basically iterates and for 5 times it adds a function to an array called operations. This function console logs the loop index variable i .

Later it runs these functions.

The expected result here should be:

0
1
2
3
4

but actually what happens is this:

5
5
5
5
5

Why is this the case? Because of the use of var .

Since var declarations are hoisted , the above code equals to

var i;
const operations = []

for (i = 0; i < 5; i++) {
  operations.push(() => {
    console.log(i)
  })
}

for (const operation of operations) {
  operation()
}

so, in the for-of loop, i is still visible, it’s equal to 5 and every reference to i in the function is going to use this value.

So how should we do to make things work as we want?

The simplest solution is to use let declarations. Introduced in ES6, they are a great help in avoiding some of the weird things about var declarations.

Changing var to let in the loop variable is going to work fine:

const operations = []

for (let i = 0; i < 5; i++) {
  operations.push(() => {
    console.log(i)
  })
}

for (const operation of operations) {
  operation()
}

Here’s the output:

0
1
2
3
4

How is this possible? This works because on every loop iteration i is created as a new variable each time, and every function added to the operations array gets its own copy of i .

Keep in mind you cannot use const in this case, because there would be an error as for tries to assign a new value in the second iteration.

Another way to solve this problem was very common in pre-ES6 code, and it is called Immediately Invoked Function Expression (IIFE). We’ll talk about them more in the next module.

In this case you can wrap the entire function and bind i to it. Since in this way you’re creating a function that immediately executes, you return a new function from it, so we can execute it later:

const operations = []

for (var i = 0; i < 5; i++) {
  operations.push(((j) => {
    return () => console.log(j)
  })(i))
}

for (const operation of operations) {
  operation()
}

Quiz

Welcome to the quiz! Try to answer those questions, which cover the topics of this module.

You can also write the question/answer into the Discord chat, to make sure it’s correct - other students or Flavio will check it for you!

  • write the code needed to call the bark() function if the dogIsHungry variable is true, and the sleep() function if it’s false
  • in a switch statement, why do we need to add a break or a return at the end of each switch case?
  • what are the ways we can break out of a for loop?
  • describe all the different ways to create a loop in JavaScript
  • explain in your own words how can “immediately invoked function expressions” help when it comes to using the loop index inside a function, inside the loop. Why do we have to wrap them in an additional function?