DEV Community

Cover image for How Control Flow Works in JavaScript
Angeline Wang
Angeline Wang

Posted on

How Control Flow Works in JavaScript

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*
}
Enter fullscreen mode Exit fullscreen mode
Example Use Case
if (i % 2 === 0) {
    console.log(i);
}
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode
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); 
Enter fullscreen mode Exit fullscreen mode
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}`
}
Enter fullscreen mode Exit fullscreen mode
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 ___
Enter fullscreen mode Exit fullscreen mode

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;}
Enter fullscreen mode Exit fullscreen mode
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’;
}
Enter fullscreen mode Exit fullscreen mode

The above code would be translated as such with a ternary operator:

const status = age > 18 ? ‘adult’: ‘child’;
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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(“”);
Enter fullscreen mode Exit fullscreen mode

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());
}
Enter fullscreen mode Exit fullscreen mode

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 cases.

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’);
}
Enter fullscreen mode Exit fullscreen mode

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 cases, 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’);
}
Enter fullscreen mode Exit fullscreen mode

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 cases 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); 
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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--;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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*
}
Enter fullscreen mode Exit fullscreen mode

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}`);
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
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;
}
Enter fullscreen mode Exit fullscreen mode

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]);
}
Enter fullscreen mode Exit fullscreen mode

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 
}
Enter fullscreen mode Exit fullscreen mode

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”
Enter fullscreen mode Exit fullscreen mode

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”
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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));
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
Use Case Example
angelineLoop:
while (isAngeline) {
    doSomething();
}
Enter fullscreen mode Exit fullscreen mode

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 undefinedin 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”
}
Enter fullscreen mode Exit fullscreen mode

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”]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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);
    });
}
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode
Example Use Case
for (let i = 0; i < events.length; i++) {
    if (events[i] === ‘party’) {
        break;
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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
Conditional (ternary) operator: MDN Web Docs
Expressions and operators: MDN Web Docs
The && and
|| Operators in JavaScript: Marius Schulz

JavaScript for… of Loop: Programix
function*: MDN Web Docs
this: MDN Web Docs
Learn ES6 The Dope Way Part II: Arrow functions and the ‘this’ keyword
For vs forEach() vs for/in vs for/of in JavaScript
Loops and iteration: MDN Web Docs
How To Use the Switch Statement in JavaScript: DigitalOcean

Oldest comments (1)

Collapse
 
fruntend profile image
fruntend

Сongratulations 🥳! Your article hit the top posts for the week - dev.to/fruntend/top-10-posts-for-f...
Keep it up 👍