How Control Flow Works in JavaScript
Tags: javascript, beginners, tutorial, codenewbie
Published: 2022-11-21 18:43:13

## Introduction: What is Control Flow?

A computer program is an algorithm that contains a sequence of events. And control flow is set up to execute those events in the correct order. Throughout this sequence of events, decisions are made based on inputs. In colloquial terms, it sounds like: “If this, then… If that, then that…”

Everything discussed below are control flow statements, which you can use to change and interfere with the normal execution of code in JavaScript. 

These are all useful ways to write code that can be reused throughout a program, which will help you finish your app faster, make your codebase more concise and run more efficiently. 

In this article, you will learn:
* What are conditionals?
* What are the different ways you can write conditionals in JavaScript?
* When and why should you use each type of conditional
* What are loops?
* What kind of loops are available in JavaScript?
* When and why should you use each type of loop

### What are Conditionals? 

Conditionals are used to decide which certain code blocks are executed, based on the evaluation of a condition. This makes a program more dynamic as it is able to react differently depending on the circumstances at runtime. 

There are three different ways you can write conditionals in JavaScript…

#### Types of Conditional Statements
* `if else` statements
* Ternary operators
* `switch` statements

#### `if else` Statements
The purpose of `if else` statements is to make binary decisions–either `true` or `false`–that tell a computer to do something. _All_ decisions are technically binary, and a final decision can be delineated from a series of binary decisions. 

##### Syntax of `if else` Statements
```
if(*condition*) {
*code block*
}

else if(*condition*) {
*code block*
}

else {
*code block*
}
```

##### Example Use Case
```
if (i % 2 === 0) {
	console.log(i);
}
```

A condition is a small equation evaluated into a Boolean result, and it is evaluated at the time the program teaches that block of code. When using `if else` statements, you can add as many conditions as you want. 

Any string, and indeed anything that is not a _falsy_ value, will be evaluated as `true`. And in order to turn any truthy value into a falsy value, simply use a bang operator like so:

```
if (!a)
```

To combine conditions, you can add multiple into one conditional statement by using the AND operator `&&` or the OR operator `||`. 

#### Ternary Operators
Ternary operators are a shorthand way to write `if else` conditional statements. What’s special about them is that they are also _right-associative_, which means that they can be chained. 

##### When to Use Ternary Operators
They are especially helpful for condensing code in cases where you only have one line of code to run after the evaluation of a condition. This may mean assigning a value or an increment. 

Another case where they are helpful is in handling `null` values. 

##### Syntax of Ternary Operators 
The ternary operator is the only JavaScript operator that takes 3 operands: 
1. Condition followed by a question mark `?` 
2. Expression to execute if Condition is truthy followed by a colon `:` 
3. Expression to execute if Condition is falsey

Here’s what it looks like:
```
condition ? exprIfTrue : exprIfFalse;
```

##### Example Use Case
This code logs the result of the ternary operator evaluation:
```
var age = 26;
var beverage = (age >= 21) ? “Beer” : “Juice”;
console.log(beverage); 
```

##### Handling `null` Values
Here’s a code example that allows an operation in the case of a `null` value by replacing the variable that is `null`:
```
let greeting = person => 
	let name = person ? person.name : `stranger`
	return `Howdy, ${name}`
}
```

##### Combining Conditions 
To combine conditions while using a ternary operator, you must add parentheses around each additional condition, with an intervening AND operator `&&` or an OR operator `||`. 

##### Combining Operations 
Furthermore, while adding multiple operations, ternary operators can be chained like so:
```
return condition1 ? value 1  // If ___ then ___
	: condition2 ? value2 // else if ___ then ___
	: condition3 ? value3 // else if ___ then ___
	: value4; // else ___
```

The equivalent operations would need to be written in far more elaborate form using `if else` statements, and would look like this:
```
if (condition1) {return value1;}
else if (condition2) {return value2;}
else if (condition3) {return value3;}
else {return value4;}
```

##### Refactoring `if else` Statements
Here’s a situation where a ternary operator could reduce the code written:
```
let status; // Variable waiting to be assigned a value 
if(age > 18) {
status = ‘adult’;
} else {
status = ‘child’;
}
```

The above code would be translated as such with a ternary operator:
```
const status = age > 18 ? ‘adult’: ‘child’;
```

The first value after `?` is returned if `true`, and the second value is returned if `false`. Placed between the `=` and the `>` is the condition. 

#### Comparison Operators
Comparison operators always return a boolean value, and they are used in many languages, including JavaScript. 

##### Types of Comparison Operators
* `>` Greater than
* `<` Less than
* `>=` Greater than or equal to 
* `<=` Less than or equal to
* `==` Loosely equal to
  * This operator converts values into the same type.
* `!=` Loosely not equal to
* `===` Strictly equal to
  * This operator checks for the same value and the same type. 
* `!==` Strictly not equal to 

#### Logical Operators
Local operators are used to connect two or more expressions in order to create a compound expression whose value is derived solely from the meaning of the original expressions and the operator(s) used.

##### Types of Logical Operators
* And operator
* Or operator

##### `&&` AND Operator
The AND operator requires both values to be `true` in order to return `true`. And the order of the two values does not affect the outcome. The operator does not check the second condition if the first is `false`. 

##### `||` OR Operator
The OR operator required either value to be `true` to return `true`. And it stops at the first condition evaluating as `true`. 

##### Non-Boolean Values 
Using logical operators in JavaScript involves different semantics compared to other C-like languages. 

This is because logical operators can operate on expressions of any type in JavaScript, not _just_ booleans. Following this, logical operators in JavaScript do not always return a boolean value. The value returned will always be the value of one of the two operand expressions. 

##### Return from Logical Operators
`&&` and `||` return the value of only one of their operands. This means that `A && B` returns the value `A` if `A` can be coerced into `false`; otherwise, it returns `B`. `A || B` returns the value `A` if `A` can be coerced into `true`; otherwise, it returns `B`. 

However, in practice, it is difficult to notice that `&&` and `||` don’t consistently return boolean values. `if` statements and loops execute when a condition evaluates to a truthy value, which means it does not need to be an actual boolean. 

##### Complications with Non-Boolean Values Returned
For example, in a web application where users can be signed out and the `user` object is `null`, or they can be signed in and the `user` object exists and has an `isAdmin` property. 

To verify if the current user is an administrator, checking if the user is authenticated is necessary. This means checking if the `user` object is not `null`. And then, you would check the `isAdmin` property to see if it's truthy. 

In the case that the `user` object is not `null` and the `user` is an administrator, the `user` object would be returned, rather than `true`. The benefit is that this saves you the hassle of needing to check its existence and grabbing the data in separate operations. And in the case that the `user` object is `null`, `null` will be returned, rather than `false`. 

However, this may be a problem if you have an `isAdministrator` function which is meant to evaluate both conditions and return a boolean value because the prefix of the function is `is`, thus concluding its purpose of returning `true` or `false`. 

##### Convert Truthy/Falsy Value into a Proper Boolean
###### Option 1: Implicit Coercion
You can coerce a value into a boolean by using the logical NOT operator, which is the bang operator, and apply it twice.

_Use Case Example_
```
function isAdministrator(user) {
	return !!(user && user.isAdmin);
}
```

_Double Bang Operator_
The first `!` returns the value `false` if the value can be coerced into `true`. If the value cannot be coerced into `true`, it will return `true`. What is returned is always a proper boolean, however the truthiness of the value is reversed. And then the second `!` undoes the reversal of the truthiness of the value. 

###### Option 2: Explicit Coercion 
You can explicitly coerce a value into a boolean by calling the `Boolean` Function. 

_Use Case Example_
```
function isAdministrator(user) {
	return Boolean(user && user.isAdmin);
}
```

#### Switch Statements
Switch statements are another way to write a conditional, but it is less used. It takes in the variable and compares it to the value of each `case`. 

It is a type of conditional statement that evaluates an expression against multiple possible cases and executes one or more blocks of code depending on which cases match. Using switch statements is like using a conditional statement with numerous `else if` blocks.

##### Related Keywords for Switch Statements
* `case`
* `break`
* `default`

##### Syntax of `switch` Statements 
A `switch` statement is always applied using `switch () {}`.

Inside the parentheses is the expression to be tested. And inside the curly braces are the cases along with the code blocks that may be run:

```
switch(*variable name*) {
case *value*:
console.log(“”);  // Code block executed on Truthy value
break;
case *value*:
console.log(“”);
break;
default:  // Equivalent to: “else”
console.log(“”);
```

Each `case` is evaluated by strict equality, and the cases are checked in chronological order. This example has 2 `case` statements and `default` is a fallback.

Here are the things that happen throughout the switch statement:
1. The expression is evaluated and the value is retrieved 

2. The value of the first `case` is compared with the value of the expression. If there is a match, the code block within the case will run, and the `break` keyword will end the `switch` block. 

3. If it is not matched with the first `case`, the code block within that `case` will be overlooked, and the value of the next `case` will be compared with the value of the expression.

If the value of that case matches the value of the expression, the code block will run and then exit out of the `switch` block due to the `break` statement. 

If none of the values of the cases match the value of the expression, the code block after the `default` statement will run. 

##### Use Case Example 
The following code block shows the month of the event with the `getEvent()` method, which receives the id of the event as an argument and returns an object with all the event details. On this event object, there is a `month` property, which is called, retrieving the number representing the month.

With a `switch` statement, you can print the appropriate month name. The cases will be considered in chronological order and compared with the value of the expression. 

When a matching case is discovered, the code block will run, and the `break` statement at the end of that code block will stop the program from evaluating the rest of the cases and exit the switch statement: 

```
// Set the month of the event to a variable, with 1 as January and 12 as December
const numMonth = getEvent(239).month;

switch (numMonth) {
	case 1:
		setMonth(‘January’);
		break;
case 2:
		setMonth(‘February’);
		break;
case 3:
		setMonth(‘March’);
		break;
case 4:
		setMonth(‘April’);
		break;
case 5:
		setMonth(‘May’);
		break;
case 6:
		setMonth(‘June’);
		break;
case 7:
		setMonth(‘July’);
		break;
case 8:
		setMonth(‘August’);
		break;
case 9:
		setMonth(‘September’);
		break;
	case 10:
		setMonth(‘October’);
		break;
	case 11:
		setMonth(‘November’);
		break;
	case 12:
setMonth(‘December’);
		break;
	default:
		setMonth(getCurrentMonth());
}
```

If the month of the event is `3`, then the `month` state will be set to the value of `March`. And depending on the Event id passed through the getEvent() function, the month will be set to different strings. 

The `default` block is included at the end to be executed in case of an error where a number not corresponding 1-12 is passed. 

Another use case could be to only set the value of month if the month is within the next three months in order to pull events happening soon in the future. This would mean that events would only be allowed to be in these months; and if the user input fell beyond these months, it would forcibly default to the current month. 

If the `break` keyword was not added to the end of each case code block, then none of the other `case` statements would have evaluated to `true`, but it would have been wasted energy sifting through the extra `case`s. 

To make your program faster and more efficient, include `break` statements. 

##### Switch Ranges
If you want to evaluate a range of values in a `switch` statement, instead of just one, you can do so by setting the expression to `true`, and then doing an operation within each `case` statement specifying different conditions on each `case`.

Since the match is auto-set to evaluate to `true`, now the expression after `case` will act like the condition after `if` in `if else` statements, and each `case` will be evaluated chronologically until one condition is met or the `switch` statement defaults. 

You need to evaluate each `case` to see if the expression evaluates to `true`, run the appropriate code, and then break out of the `switch` statement. 

###### Use Case Example
```
// Set the time of the event 
const time = 1800;

switch (true) {
	case time >= 1800:
		setEventTime(‘Evening’);
		break;
	case time >= 1200:
		setEventTime(‘Afternoon’);
		break;
	case time >= 0:
		setEventTime(‘Morning’);
		break;
	default:
		setEventTime(‘Ambiguous’);
}
```

The expression in the parentheses to be evaluated is `true`, meaning that any `case` that evaluates to `true` will be a match. In the code above, the `eventTime` state will be set to `‘Evening’`.  

##### Multiple Cases
If you want to have the same output for multiple different `case`s, you can use more than one `case` for each block of code.

###### Use Case Example
For example, if you want to match the time after 1800 **and** the time before 700 to `'Evening'`, you can do so like this:

```
// Get number corresponding to the time of the event
const time = getEvent(431).getTime(); 

switch (true) {
	case time >= 1800:
	case time <= 700:
		setEventTime(‘Evening’);
		break;
	case time >= 1200:
		setEventTime(‘Afternoon’);
		break;
	case time >= 0:
		setEventTime(‘Morning’);
		break;
	default:
		setEventTime(‘Ambiguous’);
}
```

The above code will result in an output setting the `eventTime` state to the time of day depending on the conditions above.

Although `1800` would evaluate to `true` for the other 2 `case`s as well, the first match is for `‘Evening’`, so that will be set to the `EventTime` variable. 

The same can be applied to normal switch cases (not switch ranges). 

#### `switch` vs `if else`
Some similarities between `switch` statements and `if else` statements include the fact that they are both evaluated from top to bottom and also that the first `true` returned is accepted. 

`switch` statements are advantageous due to their high readability and ability to evaluate the same variable at different values.

### What are Loops?

Loops loop over the same block of code until a certain condition is met. Different types of loops provide different methods of curating the beginning and end points of a loop. 

Loops will be written a lot. In every programming language, there are loops, control flow, & conditionals. And learning about loops is a part of software literacy. 

#### Types of Loops
* `do while` loop
* `while` loop
* `for` loop
* `for in` loop
* `for of` loop
* `forEach` loop

#### `do while` Loops
The `do while` loop runs a statement, and then repeats the statement(s) until a specified condition evaluates to `false`.

##### Syntax of `do while` Loops
```
do
	statement 
while (condition); 
```

The statement is always run one time before the condition is evaluated. If you want to run many statements, use a block statement `{}` to group them. If the condition is evaluated to `true`, the statement runs again. 

The condition is checked upon the _conclusion_ of each execution. When the condition returns `false`, the execution stops, and the program moves onto the code after the `do while` loop. 

##### Use Case Example 
```
let i = 0;
do {
	i += 1;
	console.log(i);
} while (i < 20);
```

This `do` loop iterates at least once and reiterates until `i` is no longer less than `20`. 

#### `while` Loops
A `while` loop continues executing its statements given that a specified condition evaluates to `true`. It checks the condition at the _start_ of each loop, and after code execution, it returns to check the condition. If the condition evaluates as `true`, it runs the code block again.

##### Syntax of `while` Loops 
```
while (condition)
	statement
```

The evaluation of the condition is done _before_ the statement in the loop is run. And if the condition returns `true`, then the `statement` is run, and the `condition` is evaluated again. If you want to run a series of statements, you can do so using a block statement `{}` to group them. 

If the condition evaluates to `false`, then the statement(s) within the loop are not executed, and the program moves onto the next line of code after the loop. 

##### Use Case Example 
```
let a = 10;
while(a > 5) {
console.log (`a is now ${a}`);
a--;
}
```

This program goes back to the top of the loop with the new value, and then checks the condition in the `()` again. After the condition is met and the loop ends, the program moves onto the next line of code after the `while` loop.

##### Use Case Example #2 
```
let n = 0;
let x = 0;
while (n < 50) {
	n++;
	x += n;
}
```

This `while` loop iterates as long as `n` is less than 50. And during each iteration, the loop increments `n` and adds that value to `x`. 

During the first execution, `n` takes the value of `1` and `x` takes the value of `1`. During the second execution, `n` takes the value of `2` and `x` takes the value of `3`. And during the third execution, `n` takes the value of `3` and `x` takes the value of `6`. 

After the code has executed 50 times, the condition `n < 50` returns `false`, and then the loop ends. 

##### Infinite Loop
Infinite loops are possible in both `while` and `do while` loops, as long as the condition is always evaluated to `true`. This allows for the code block to run for an infinite number of times, which will lead to your application crashing. 

It is important to ensure the condition in `while` and `do while` loops becomes `false` at some point. You must modify the variable after each loop, which can be done by providing an incrementer or decrementer.

#### `while` vs `do while`
The key difference between these two loops is the order of operations. 

In a `while` loop, the program first checks that the condition evaluates to `true`, then runs the code block. In a `do while` loop, first, the code block is run, and then the condition is evaluated to determine whether to rerun the code. 

This means that `do while` loops ensure the code block is run _at least_ once; and it is possible for the code in a `while` loop to never run. 

#### `while` vs `for`
The main advantage of using a `while` loop is that it is flexible. This means that it can be made dynamic to the user input. This is useful when you do not know the number of times the loop needs to be run, or when you do not know the specific intent of the loop. 

However, using a `for` loop may be better for cases that are more specific. This means that it is useful for looping over arrays, looping over a specific number of lines, or when you know the specific number of times you want to run a code block. 

#### `for` Loops
A `for` loop is similar to the `while` loop, but the syntax is different. Nonetheless, the concepts are quite straightforward. 

An advantage of `for` loops is the fact that they are self-contained, which means that they cannot accidentally have an infinite loop the way that `while` loops can. This is useful when iterating overall, or through a subset of items. 

##### Syntax of `for` Loops
```
for(*initial variable*; *condition*; *alteration*) {
*code block = directs logic of code: tells app how to make a decision*
}
```

Remember that semicolons are necessary within the parentheses `()` after `for` and `while`, but they are not necessary within the curly braces `{}` after the parentheses `()`.

##### `for` Loop Condition
There are 3 parts of a `for` loop condition. First, the initialisation of a variable to use as part of the condition. Second, the actual condition that’ll evaluate `true` or `false`. And third, the incrementer to bring you closer to the end of the condition. 

The incrementing will continue until the condition evaluates to `false`, and at which point, the program moves onto the rest of the code. 

##### Example Use Case
```
for(let a = 1; a < 5; a++) {
console.log(`a is now ${a}`);
}
```

The block of code will continue to run as long as the `for` loop condition returns `true`. 

#### `for in` Loops 
`for in` loop iterates a specified variable over all the keys inside an Object that are **both** user-defined **and numeric indices**. 

For each distinct property, JavaScript runs the specified statements inside the `for in` loop. 

##### Syntax of `for in` Loops
```
for (variable in object) 
	statement
```

##### Use Case Example
This function takes an argument of an object and the object’s name. Then, it iterates over all the parent object’s properties and returns a string that lists the property names and values:  
```
function newProps(object, objectName) {
	let result = ‘’;
	for (const i in object) {
		result += `${objectName}.${i} = ${obj[i]}<br>`;
	}
	result += ‘<hr>’;
	return result;
}
```

For an Object named `event` with properties `month` and `year`, `result` returned would be:
`event.month = September`
`event.year = 2022`

Technically, user-defined properties (like custom properties or methods) can be assigned to arrays, so to ensure only numerically indexed array elements are accessed, rather than accessing both user-defined and numeric indices, use a `for of` loop instead.

`for in` loops can be used for both iterables and objects; nonetheless, using `for in` loops on iterables should be avoided. 

#### `Object.keys()` vs `for in`
A `for in` loop has the same utility as `Object.keys()`. If you only want to return the keys of an object, `Object.keys()` may be the way to go. 

However, if you wish also to perform an operation for each element iterated through, `for in` will be the faster way to do so, since using `Object.keys()` and then a `forEach()` requires passing a callback during _every_ iteration.

#### `for` vs `for in`
Both `for` and `for in` loops give access to the index of each element in an array and not the actual element value. This means that you still need to use the array name, followed by the given index, in order to find the element value. 

##### Use Case Example
```
const arr = [‘Angeline’, ‘Sarah’, ‘Laura’]

for (let i = 0; i < arr.length; ++i) {
	console.log(arr[i]);
}

for (let i in arr) {
	console.log(arr[i]);
}
```

#### `for of` Loops
`for of` statements create a loop that iterates over iterable objects. It will invoke a custom iteration hook with statements to be executed for the value of each distinct property. 

The difference between a `for of` loop and a `for in` loop is that a `for of` loop iterates over property _values_, instead of names. 

`for of` loops (without additional aid) cannot be used to iterate over an object, it can only be used to iterate over iterable objects. 

##### When to use `for of` loops
`for of` loops are the best way to iterate over an array in JavaScript because they are more concise than the conventional `for` loop and **also** does not have as many edge cases as `for in` and `forEach()` loops. 

However, `for of` loops do take extra work to access element indices, and you cannot chain them like you can do with `forEach()` loops. 

##### Syntax of `for of` Loops
```
for (const element of array) {
	console.log(element)

// Each array element is being assigned to the variable named “element” during each iteration until all the elements have been iterated upon 
}
```

The first part within the parentheses before `of` is a variable declaration (with anything before the `=` operator), which can be done with `let` `var` or `const`. 

`const` can be used so long as the variable is not reassigned within the code block. Between iterations, the function context is considered new, so it is okay to use `const`. 

If the elements within the iterable are objects, you can also destructure the object property like so: `for (event.date of events)`.

#### `for of` vs `for in`
There are two important factors that differ in terms of `for of` and `for in` loops: 

First is what **part** of each element they iterate over and return. `for in` loops iterate over the _keys_ of each element, while `for of` loops iterate over the _values_ of each element.

Second is what **kind** of elements they include in their operations.  `for of` loops only iterate through elements with _numeric_ array index keys, so they only work for array-type objects. 

`for in` loops do not discriminate based on the origins of keys, and include all elements, with either _numeric_ array indices as keys **and** _user-generated_ elements with string keys. 

##### Comparison Example
```
const arr = [1, 2, 3];
arr.greeting = ‘good afternoon’;

// for of 
for (const i of arr) {
	console.log(i);
}
// Prints: “1” “2” “3” 

// for in
for (const i in arr) {
	console.log(i);
}
// Prints: “0” “1” “2” “greeting”
```

If you wish to return the _values_ of elements including those with _user-generated_ string keys, you can do so by using a `for in` loop with **bracket notation**. All keys, including user-generated ones, can be passed inside the brackets after the object name, like so:

```
const arr = [1, 2, 3];

arr.greeting = “good afternoon”;

for (const i in arr) {
	console.log(arr[i])
} // Prints: “1” “2” “3” “good afternoon”
```

It seems there is no premeditated way to return only the _keys_ that are numeric array index keys. 

#### `Object.entries()`
Both `for of` and `for in` loops can also be used with destructuring. For example, they can both simultaneously loop over keys and values of an object with `Object.entries()`.

With the use of `Object.entries()`, an array (with numeric indices as keys) is created from an object (previously array or non-array). This means that by replacing the `arr` with `Object.entries(arr)`, even an object with _user-generated_ string keys can be used with `for of`, like so:

```
// for of 
const obj = { angeline: 10, laura: 20 };

for (const [key, val] of Object.entries(obj)) {
	console.log(key, val);
}
// Prints: 
// “angeline” 10
// “laura” 20
```

#### `forEach` Loops
`forEach` loops are useful for iterating through all the items in a collection. It is read-only, meaning it cannot modify items as they are being iterated through. 

##### When to Use `forEach` Loops
A good rule of thumb is to use `forEach` sparingly because it comes with many edge cases. However, there are still many cases where it can be used to make code more concise.

##### Syntax of `forEach` Loops 
```
const guests = [‘Angeline’, ‘Laura’, ‘Sarah’];

guests.forEach(guest => console.log(guest));
```

The `forEach` loop takes a callback function that is used on each element within the array. The callback function takes a mandatory current value parameter, and optionally, the index and the array object. 

#### `forEach` vs `for of`
One similarity between `forEach` and `for of` is the fact that they both access the array element value itself. One difference is that the `forEach` loop can access both the value and the index, which can be passed in as the second parameter.

##### Comparison Example 
```
arr.forEach((v, i) => console.log(v));

for (const v of arr) {
	console.log(v);
}
```

One case where you would choose to use `for of` instead of `forEach` is if you want to ensure prevention of “holes” in your user data, which can result from empty array elements. 

#### Labeled Statements
You can add a `label` to a statement to act as an identifier you can reference elsewhere in your program. 

##### When to Use Labeled Statements
A label is useful for identifying a loop, and then to use `break` or `continue` statements to show if the program should interrupt the loop or continue its execution. 

##### Syntax of Labeled Statements
```
label: // This can be any JS identifier, excluding reserved words
	statement // Can be any statement
```

##### Use Case Example
```
angelineLoop:
while (isAngeline) {
	doSomething();
}
```

#### Non-Numeric Properties
Since JavaScript arrays are objects, it is possible to also add string properties to an array, not just the number indices. 

`for of`, `for` and `forEach` loops all ignore non-numeric properties and print out _only_ numeric properties; however, `for in` loops will print out _both_ numeric _and_ non-numeric properties. 

This means that by default, it is best to use `for of` loops on arrays with only numeric properties. 

#### Empty Elements 
You are allowed to have empty elements in a JavaScript array, but the 4 looping constructs will handle empty elements differently. `for in` and `forEach` loops will skip the empty element; while `for` and `for of` loops do not skip the empty element, and instead prints out `undefined`in its place.

##### Use Case Example 
```
const arr = [‘Angeline’, , ‘Laura’];

arr.length; // 3

for (let i = 0; i < arr.length; ++i) {
	console.log(arr[i]); // Prints “Angeline, undefined, Laura”
}

arr.forEach(v => console.log(v)); // Prints “Angeline, Laura”

for (let i in arr) {
	console.log(arr[i]); // Prints “Angeline, Laura”
}

for (const v of arr) {
	console.log(v); // Prints “Angeline, undefined, Laura”
}
```

This behaviour only occurs when there is literally an empty array element. If the array element has a value of `undefined`, the 4 looping constructs will universally print `“Angeline, undefined, Laura”`.

##### Add Empty Array Element 
If an element at a larger index than the logical subsequent is added, the intermediary indices in the array will be filled with empty elements.

###### Use Case Example
```
const arr = [“Angeline”, “Sarah”, “Laura”];
arr[5] = “Maya”;

// Equivalent to [“Angeline”, “Sarah”, “Laura”, , “Maya”]
```

`forEach()` and `for in` loops skip empty elements in the array. `for` and `for of` loops do not. 

##### Interaction with JSON
However, empty elements are not supported in JSON, and will be marked as a `SyntaxError: Unexpected token`. Thus, it is rare that empty elements will be used. 

Likely, `null` will be used in place of any empty elements. This means that the behaviour of `forEach()` and `for in` loops when treating empty elements will rarely cause a practical issue. 

When `for…in` and `forEach()` loops skip empty elements in an array, this behaviour is called “holes”. Any “holes” should likely be treated as having a value of `undefined`. 

It is possible to disallow using `forEach()` in order to prevent cases of holes. You can write a `.eslintrc.yml` file with the following content:

```
parserOptions:
	ecmaVersion: 2018
rules:
	no-restricted-syntax:
		- error
		- selector: CallExpression[callee.property.name="forEach"]
		  message: Do not use `forEach()`, use `for/of` instead
```

#### Function Context 
`for`, `for in` and `for of` loops retain the outside scope’s value of `this`. And the `forEach()` callback will have a different `this` unless you use an array function. 

If you want to ensure that all 4 types of loops access the same value for `this`, you need to enforce a rule to only allow `forEach()` loops to be used with arrow functions. This can be done through the `no-arrow-callback` ESLint rule to require arrow functions for all callbacks that don’t use `this`. 

#### Async/Await Generators
`forEach()` loops do not precisely work with async/await or generators. Inside an `async` function that has a `forEach()` callback function, you cannot use an `await`, nor can you use a `yield`. 

Syntax errors will be thrown if code like below is written: 
```
async function run() {
	const arr = [ ‘Angeline’, ‘Sarah’, ‘Laura’];
	arr.forEach(el => {
		// SyntaxError
		await new Promise(resolve => setTimeout(resolve, 1000));
		console.log(el);
	});
}

function* run() {
	const arr = [‘Angeline’, ‘Sarah’, ‘Laura’];
	arr.forEach(el => {
		yield new Promise(resolve => setTimeout(resolve, 1000));
		console.log(el);
	});
}
```

However, the 2 code examples do work with `for of` loops, like this:
```
async function asyncFn() {
	const arr = [‘Angeline’, ‘Sarah’, ‘Laura’’];
	for (const el of arr) {
		await new Promise(resolve => setTimeout(resolve, 1000));
		console.log(el);
	}
}

function* generatorFn() {
	const arr = [‘Angeline’, ‘Sarah’, ‘Laura’];
	for (const el of arr) {
		yield new Promise(resolve => setTimeout(resolve, 1000));
		console.log(el);
	}
}
```

When using async/await or generators, `forEach()` is syntactic sugar, so it should be used sparingly and not for everything.

### How to Control Loops

There are 2 control flow statements that can be used as operators to further manipulate the execution of code within loops:
* `break` statements
* `continue` statements

These can be applied to `switch` statements or any of the loops (excluding `forEach`) to alter the normal control flow of the loop. Where a statement is exited prematurely, clean-up will be performed with the `return()` method of the iterator.

#### `break` Statements
The purpose of `break` statements is to terminate a loop, a `switch` statement, or it can be used with a labeled statement.

##### Syntax of `break` Statement
```
break;
break label;
```

##### Example Use Case 
```
for (let i = 0; i < events.length; i++) {
	if (events[i] === ‘party’) {
		break;
	}
}
```

This iterates through the elements in an array until the program finds the index of an element whose value is `‘party’`. 

##### Using `break` without a Label
If you use a `break` statement without a label, you will immediately stop the innermost enclosing `while`, `do while`, `for` or `switch` and move onto the next line of code after the `while`, `do while`, `for` or `switch` statement.

##### Using `break` with a Label
If you use a `break` statement with a label, you will stop the specified labeled statement and move onto the line of case after the `break` statement. 

##### Break to a Label
```
let x = 0;
let z = 0;
labelCancelLoops: while (true) {
	console.log(‘Outer loops: ’, x);
	x += 1;
	z = 1;
	while (true) {
		console.log(‘Inner loops: ’, z);
		z += 1;
		if (z === 10 && x === 10) {
			break labelCancelLoops;
		} else if (z === 10) {
			break;
		}
	}
}
```

#### `continue` Statements
A `continue` statement can be applied to restart a `while`, `do while`, `for` loop or `label` statement. 

##### Syntax of `continue` Statement 
```
continue;
continue label;
```

`continue` will end the current iteration (leaving remaining code in the block untouched), but it will go back to the start of the loop at the condition and run the loop again. It will not completely break out of the loop as a whole onto the next line of code, it just breaks out of the current _iteration_ of the loop. 

##### Use Case Example 
Here is a `while` loop with a `continue` statement that runs when the value of `i` is `5`. `n` takes on the values `1`, `3`, `6`, `10`, `16`, `23`, `31`, `40`, `50`: 

```
let i = 0;
let n = 0;
while (i < 10) {
	i++;
	if (i === 5) {
		continue;
	}
	n += i;
	console.log(n);
}

//1,3,6,10,16,23,21,3,0,40
```

If `continue` is removed, the loop would instead print: `1,3,6,10,15,21,28,36,45,55`.

##### Use Case Example #2
Here is a `while` loop labeled `checkxandy` includes another while loop labeled `checky`. If `continue` is encountered, the program ends the current iteration of `checky` and starts the next iteration:

```
let x = 0;
let y = 10;
checkxandy: while (x < 4) {
	console.log(x);
	x += 1;
	checky: while (y > 4) {
		console.log(y);
		y -= 1;
		if ((y % 2) === 0) {
		continue checky;
} 
console.log(y, ‘ is odd.’);
	}
	console.log(‘x = ‘, x);
	console.log(‘y = ’, y);
}
```

Every time `continue` is encountered, `checky` reiterates until its condition returns `false`. When `false` is returned, the remainder of `checkxandy` is run and `checkxandy` reiterates until its condition returns `false`. When `false` is returned from `checkxandy`, the program continues onto next line of code after `checkxandy`.

If `continue` had a label of `checkxandy`, the program would continue at top of `checkxandy` loop. 

##### `continue` without Label
Using `continue` without a label will end the _current_ iteration of the innermost enclosing `while`, `do while`, or `for` statement and continue the execution of the loop with the _next_ iteration.

##### `continue` with Label
Using `continue` with a label will apply it to the looping statement identified with that label.

#### `continue` vs `break`
`continue` does not end the loop entirely. In a `while` loop, `continue` will lead the program to jump back to the `while` loop’s condition. And in a `for` loop, `continue` will lead the program to jump to the “increment-expression”.

## Conclusion 

In this article, we when over what control flow is and different ways we can manipulate control flow in JavaScript through conditionals and loops. 

A wide range of use cases were covered with the functionality of `if else`, ternary operators, and `switch` statements, in order to alter code blocks executed during runtime conditionally. We also discussed the varying flexibility of using `while` loops and `for` loops. 

To explore these topics in more detail, I've added some useful links below. 

Happy learning! 

## Resources for Further Exploration
[Conditionals - Learn: JavaScript.com](https://www.javascript.com/learn/conditionals)
[Conditional (ternary) operator: MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator)
[Expressions and operators: MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators)
[The && and 
|| Operators in JavaScript: Marius Schulz](https://mariusschulz.com/blog/the-and-and-or-operators-in-javascript)
[JavaScript for… of Loop: Programix](https://www.programiz.com/javascript/for-of)
[function*: MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*)
[this: MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#examples)
[Learn ES6 The Dope Way Part II: Arrow functions and the ‘this’ keyword](https://www.freecodecamp.org/news/learn-es6-the-dope-way-part-ii-arrow-functions-and-the-this-keyword-381ac7a32881/)
[For vs forEach() vs for/in vs for/of in JavaScript](https://thecodebarbarian.com/for-vs-for-each-vs-for-in-vs-for-of-in-javascript)
[Loops and iteration: MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration#do...while_statement)
[How To Use the Switch Statement in JavaScript: DigitalOcean](https://www.digitalocean.com/community/tutorials/how-to-use-the-switch-statement-in-javascript)