Linux Professional Institute Learning Logo.
Skip to main content
  • Home
    • All Resources
    • LPI Learning Materials
    • Become a Contributor
    • Publishing Partners
    • Become a Publishing Partner
    • About
    • FAQ
    • Contributors
    • Roadmap
    • Contact
  • LPI.org
034.3 Lesson 1
Topic 031: Software Development and Web Technologies
031.1 Software Development Basic
  • 031.1 Lesson 1
031.2 Web Application Architecture
  • 031.2 Lesson 1
031.3 HTTP Basics
  • 031.3 Lesson 1
Topic 032: HTML Document Markup
032.1 HTML Document Anatomy
  • 032.1 Lesson 1
032.2 HTML Semantics and Document Hierarchy
  • 032.2 Lesson 1
032.3 HTML References and Embedded Resources
  • 032.3 Lesson 1
032.4 HTML Forms
  • 032.4 Lesson 1
Topic 033: CSS Content Styling
033.1 CSS Basics
  • 033.1 Lesson 1
033.2 CSS Selectors and Style Application
  • 033.2 Lesson 1
033.3 CSS Styling
  • 033.3 Lesson 1
033.4 CSS Box Model and Layout
  • 033.4 Lesson 1
Topic 034: JavaScript Programming
034.1 JavaScript Execution and Syntax
  • 034.1 Lesson 1
034.2 JavaScript Data Structures
  • 034.2 Lesson 1
034.3 JavaScript Control Structures and Functions
  • 034.3 Lesson 1
  • 034.3 Lesson 2
034.4 JavaScript Manipulation of Website Content and Styling
  • 034.4 Lesson 1
Topic 035: NodeJS Server Programming
035.1 NodeJS Basics
  • 035.1 Lesson 1
035.2 NodeJS Express Basics
  • 035.2 Lesson 1
  • 035.2 Lesson 2
035.3 SQL Basics
  • 035.3 Lesson 1
How to get certified
  1. Topic 034: JavaScript Programming
  2. 034.3 JavaScript Control Structures and Functions
  3. 034.3 Lesson 1

034.3 Lesson 1

Certificate:

Web Development Essentials

Version:

1.0

Topic:

034 JavaScript Programming

Objective:

034.3 JavaScript Control Structures and Functions

Lesson:

1 of 2

Introduction

Like any other programming language, JavaScript code is a collection of statements that tells an instruction interpreter what to do in sequential order. However, this does not mean that every statement should be executed only once or that they should execute at all. Most statements should execute only when specific conditions are met. Even when a script is triggered asynchronously by independent events, it often has to check a number of control variables to find out the right portion of code to run.

If Statements

The simplest control structure is given by the if instruction, which will execute the statement immediately after it if the specified condition is true. JavaScript considers conditions to be true if the evaluated value is non-zero. Anything inside the parenthesis after the if word (spaces are ignored) will be interpreted as a condition. In the following example, the literal number 1 is the condition:

if ( 1 ) console.log("1 is always true");

The number 1 is explicitly written in this example condition, so it is treated as a constant value (it remains the same throughout the execution of the script) and it will always yield true when used as a conditional expression. The word true (without the double quotes) could also be used instead of 1, as it is also treated as a literal true value by the language. The console.log instruction prints its arguments in the browser’s console window.

Tip

The browser console shows errors, warnings, and informational messages sent with the console.log JavaScript instruction. On Chrome, the kbd:[Ctrl+Shift+J] (kbd:[Cmd+Option+J] on Mac) key combination opens the console. On Firefox, the kbd:[Ctrl+Shift+K] (kbd:[Cmd+Option+K] on Mac) key combination opens the console tab in the developer’s tools.

Although syntactically correct, using constant expressions in conditions is not very useful. In an actual application, you’ll probably want to test the trueness of a variable:

let my_number = 3;
if ( my_number ) console.log("The value of my_number is", my_number, "and it yields true");

The value assigned to the variable my_number (3) is non-zero, so it yields true. But this example is not common usage, because you rarely need to test whether a number equals zero. It is much more common to compare one value to another and test whether the result is true:

let my_number = 3;
if ( my_number == 3 ) console.log("The value of my_number is", my_number, "indeed");

The double equal comparison operator is used because the single equal operator is already defined as the assignment operator. The value on each side of the operator is called an operand. The ordering of the operands does not matter and any expression that returns a value can be an operand. Here a list of other available comparison operators:

value1 == value2

True if value1 is equal to value2.

value1 != value2

True if value1 is not equal to value2.

value1 < value2

True if value1 is less than value2.

value1 > value2

True if value1 is greater than value2.

value1 <= value2

True if value1 is less than or equal to value2.

value1 >= value2

True if value1 is grater than or equal to value2.

Usually, it does not matter whether the operand to the left of the operator is a string and the operand to the right is a number, as long as JavaScript is able to convert the expression to a meaningful comparison. So the string containing the character 1 will be treated as the number 1 when compared to a number variable. To make sure the expression yields true only if both operands are of the exact same type and value, the strict identity operator === should be used instead of ==. Likewise, the strict non-identity operator !== evaluates as true if the first operand is not of the exact same type and value as the second operator.

Optionally, the if control structure can execute an alternate statement when the expression evaluates as false:

let my_number = 4;
if ( my_number == 3 ) console.log("The value of my_number is 3");
else console.log("The value of my_number is not 3");

The else instruction should immediately follow the if instruction. So far, we are executing only one statement when the condition is met. To execute more than one statement, you must enclose them in curly braces:

let my_number = 4;
if ( my_number == 3 )
{
  console.log("The value of my_number is 3");
  console.log("and this is the second statement in the block");
}
else
{
  console.log("The value of my_number is not 3");
  console.log("and this is the second statement in the block");
}

A group of one or more statements delimited by a pair of curly braces is known as a block statement. It is common to use block statements even when there is only one instruction to execute, in order to make the coding style consistent throughout the script. Moreover, JavaScript does not require the curly braces or any statements to be on separate lines, but to do so improves readability and makes code maintenance easier.

Control structures can be nested inside each other, but it is important not to mix up the opening and closing braces of each block statement:

let my_number = 4;

if ( my_number > 0 )
{
  console.log("The value of my_number is positive");

  if ( my_number % 2 == 0 )
  {
    console.log("and it is an even number");
  }
  else
  {
    console.log("and it is an odd number");
  }
} // end of if ( my_number > 0 )
else
{
  console.log("The value of my_number is less than or equal to 0");
  console.log("and I decided to ignore it");
}

The expressions evaluated by the if instruction can be more elaborate than simple comparisons. This is the case in the previous example, where the arithmetical expression my_number % 2 was employed inside the parentheses in the nested if. The % operator returns the remainder after dividing the number on its left by the number on its right. Arithmetical operators like % take precedence over comparison operators like ==, so the comparison will use the result of the arithmetical expression as its left operand.

In many situations, nested conditional structures can be combined into a single structure using logical operators. If we were interested only in positive even numbers, for example, a single if structure could be used:

let my_number = 4;

if ( my_number > 0 && my_number % 2 == 0 )
{
  console.log("The value of my_number is positive");
  console.log("and it is an even number");
}
else
{
  console.log("The value of my_number either 0, negative");
  console.log("or it is a negative number");
}

The double ampersand operator && in the evaluated expression is the logical AND operator. It evaluates as true only if the expression to its left and the expression to its right evaluate as true. If you want to match numbers that are either positive or even, the || operator should be used instead, which stands for the OR logical operator:

let my_number = -4;

if ( my_number > 0 || my_number % 2 == 0 )
{
  console.log("The value of my_number is positive");
  console.log("or it is a even negative number");
}

In this example, only negative odd numbers will fail to match the criteria imposed by the composite expression. If you have the opposite intent, that is, to match only the negative odd numbers, add the logical NOT operator ! to the beginning of the expression:

let my_number = -5;

if ( ! ( my_number > 0 || my_number % 2 == 0 ) )
{
  console.log("The value of my_number is an odd negative number");
}

Adding the parenthesis in the composite expression forces the expression enclosed by them to be evaluated first. Without these parentheses, the NOT operator would apply only to my_number > 0 and then the OR expression would be evaluated. The && and || operators are known as binary logical operators, because they require two operands. ! is known as a unary logical operator, because it requires only one operand.

Switch Structures

Although the if structure is quite versatile and sufficient to control the flow of the program, the switch control structure may be more appropriate when results other than true or false need to be evaluated. For example, if we want to take a distinct action for each item chosen from a list, it will be necessary to write an if structure for each assessment:

// Available languages: en (English), es (Spanish), pt (Portuguese)
let language = "pt";

// Variable to register whether the language was found in the list
let found = 0;

if ( language == "en" )
{
  found = 1;
  console.log("English");
}

if ( found == 0 && language == "es" )
{
  found = 1;
  console.log("Spanish");
}

if ( found == 0 && language == "pt" )
{
  found = 1;
  console.log("Portuguese");
}

if ( found == 0 )
{
  console.log(language, " is unknown to me");
}

In this example, an auxiliary variable found is used by all if structures to find out whether a match has occurred. In a case such as this one, the switch structure will perform the same task, but in a more succinct manner:

switch ( language )
{
  case "en":
    console.log("English");
    break;
  case "es":
    console.log("Spanish");
    break;
  case "pt":
    console.log("Portuguese");
    break;
  default:
    console.log(language, " not found");
}

Each nested case is called a clause. When a clause matches the evaluated expression, it executes the statements following the colon until the break statement. The last clause does not need a break statement and is often used to set the default action when no other matches occur. As seen in the example, the auxiliary variable is not needed in the switch structure.

Warning

switch uses strict comparison to match expressions against its case clauses.

If more than one clause triggers the same action, you can combine two or more case conditions:

switch ( language )
{
  case "en":
  case "en_US":
  case "en_GB":
    console.log("English");
    break;
  case "es":
    console.log("Spanish");
    break;
  case "pt":
  case "pt_BR":
    console.log("Portuguese");
    break;
  default:
    console.log(language, " not found");
}

Loops

In previous examples, the if and switch structures were well suited for tasks that need to run only once after passing one or more conditional tests. However, there are situations when a task must execute repeatedly — in a so-called loop — as long as its conditional expression keeps testing as true. If you need to know whether a number is prime, for example, you will need to check whether dividing this number by any integer greater than 1 and lower than itself has a remainder equal to 0. If so, the number has an integer factor and it is not prime. (This is not a rigorous or efficient method to find prime numbers, but it works as a simple example.) Loop controls structures are more suitable for such cases, in particular the while statement:

// A naive prime number tester

// The number we want to evaluate
let candidate = 231;

// Auxiliary variable
let is_prime = true;

// The first factor to try
let factor = 2;

// Execute the block statement if factor is
// less than candidate and keep doing it
// while factor is less than candidate
while ( factor < candidate )
{

  if ( candidate % factor == 0 )
  {
    // The remainder is zero, so the candidate is not prime
    is_prime = false;
    break;
  }

  // The next factor to try. Simply
  // increment the current factor by one
  factor++;
}

// Display the result in the console window.
// If candidate has no integer factor, then
// the auxiliary variable is_prime still true
if ( is_prime )
{
  console.log(candidate, "is prime");
}
else
{
  console.log(candidate, "is not prime");
}

The block statement following the while instruction will execute repeatedly as long as the condition factor < candidate is true. It will execute at least once, as long as we initialize the factor variable with a value lower than candidate. The if structure nested in the while structure will evaluate whether the remainder of candidate divided by factor is zero. If so, the candidate number is not prime and the loop can finish. The break statement will finish the loop and the execution will jump to the first instruction after the while block.

Note that the outcome of the condition used by the while statement must change at every loop, otherwise the block statement will loop “forever”. In the example, we increment the factor variable‒the next divisor we want to try‒and it guarantees the loop will end at some point.

This simple implementation of a prime number tester works as expected. However, we know that a number that is not divisible by two will not be divisible by any other even number. Therefore, we could just skip the even numbers by adding another if instruction:

while ( factor < candidate )
{

  // Skip even factors bigger than two
  if ( factor > 2 && factor % 2 == 0 )
  {
    factor++;
    continue;
  }

  if ( candidate % factor == 0 )
  {
    // The remainder is zero, so the candidate is not prime
    is_prime = false;
    break;
  }

  // The next number that will divide the candidate
  factor++;
}

The continue statement is similar to the break statement, but instead of finishing this iteration of the loop, it will ignore the rest of the loop’s block and start a new iteration. Note that the factor variable was modified before the continue statement, otherwise the loop would have the same outcome again in the next iteration. This example is too simple and skipping part of the loop will not really improve its performance, but skipping redundant instructions is very important when writing efficient applications.

Loops are so commonly used that they exist in many different variants. The for loop is specially suited to iterate through sequential values, because it lets us define the loop rules in a single line:

for ( let factor = 2; factor < candidate; factor++ )
{
  // Skip even factors bigger than two
  if ( factor > 2 && factor % 2 == 0 )
  {
    continue;
  }

  if ( candidate % factor == 0 )
  {
    // The remainder is zero, so the candidate is not prime
    is_prime = false;
    break;
  }
}

This example produces the exact same result as the previous while example, but its parenthetic expression includes three parts, separated by semicolons: the initialization (let factor = 2), the loop condition (factor < candidate), and the final expression to be evaluated at the end of each loop iteration (factor++). The continue and break statements also apply to for loops. The final expression in the parentheses (factor++) will be evaluated after the continue statement, so it should not be inside the block statement, otherwise it will be incremented two times before the next iteration.

JavaScript has special types of for loops to work with array-like objects. We could, for example, check an array of candidate variables instead of just one:

// A naive prime number tester

// The array of numbers we want to evaluate
let candidates = [111, 139, 293, 327];

// Evaluates every candidate in the array
for (candidate of candidates)
{
  // Auxiliary variable
  let is_prime = true;

  for ( let factor = 2; factor < candidate; factor++ )
  {
    // Skip even factors bigger than two
    if ( factor > 2 && factor % 2 == 0 )
    {
      continue;
    }

    if ( candidate % factor == 0 )
    {
      // The remainder is zero, so the candidate is not prime
      is_prime = false;
      break;
    }
  }

  // Display the result in the console window
  if ( is_prime )
  {
    console.log(candidate, "is prime");
  }
  else
  {
    console.log(candidate, "is not prime");
  }
}

The for (candidate of candidates) statement assigns one element of the candidates array to the candidate variable and uses it in the block statement, repeating the process for every element in the array. You don’t need to declare candidate separately, because the for loop defines it. Finally, the same code from the previous example was nested in this new block statement, but this time testing each candidate in the array.

Guided Exercises

  1. What values of the my_var variable match the condition my_var > 0 && my_var < 9?

  2. What values of the my_var variable match the condition my_var > 0 || my_var < 9?

  3. How many times does the following while loop execute its block statement?

    let i = 0;
    while ( 1 )
    {
      if ( i == 10 )
      {
        continue;
      }
      i++;
    }

Explorational Exercises

  1. What happens if the equal assignment operator = is used instead of the equal comparison operator ==?

  2. Write a code fragment using the if control structure where an ordinary equality comparison will return true, but a strict equality comparison will not.

  3. Rewrite the following for statement using the unary NOT logical operator in the loop condition. The outcome of the condition should be the same.

    for ( let factor = 2; factor < candidate; factor++ )
  4. Based on the examples from this lesson, write a loop control structure that prints all the integer factors of a given number.

Summary

This lesson covers how to use control structures in JavaScript code. Conditional and loop structures are essential elements of any programming paradigm, and JavaScript web development is no exception. The lesson goes through the following concepts and procedures:

  • The if statement and comparison operators.

  • How to use the switch structure with case, default, and break.

  • The difference between ordinary and strict comparison.

  • Loop control structures: while and for.

Answers to Guided Exercises

  1. What values of the my_var variable match the condition my_var > 0 && my_var < 9?

    Only numbers that are both greater than 0 and less than 9. The && (AND) logical operator requires both comparisons to match.

  2. What values of the my_var variable match the condition my_var > 0 || my_var < 9?

    Using the || (OR) logical operator will cause any number to match, as any number will either be greater than 0 or less than 9.

  3. How many times does the following while loop execute its block statement?

    let i = 0;
    while ( 1 )
    {
      if ( i == 10 )
      {
        continue;
      }
      i++;
    }

    The block statement will repeat indefinitely, as no stop condition was supplied.

Answers to Explorational Exercises

  1. What happens if the equal assignment operator = is used instead of the equal comparison operator ==?

    The value on the right side of the operator is assigned to the variable on the left and the result is passed to the comparison, which may not be the desired behavior.

  2. Write a code fragment using the if control structure where an ordinary equality comparison will return true, but a strict equality comparison will not.

      let a = "1";
      let b = 1;
    
      if ( a == b )
      {
        console.log("An ordinary comparison will match.");
      }
    
      if ( a === b )
      {
        console.log("A strict comparison will not match.");
      }
  3. Rewrite the following for statement using the unary NOT logical operator in the loop condition. The outcome of the condition should be the same.

    for ( let factor = 2; factor < candidate; factor++ )

    Answer:

    for ( let factor = 2; ! (factor >= candidate); factor++ )
  4. Based on the examples from this lesson, write a loop control structure that prints all the integer factors of a given number.

    for ( let factor = 2; factor <= my_number; factor++ )
    {
      if ( my_number % factor == 0 )
      {
        console.log(factor, " is an integer factor of ", my_number);
      }
    }

Linux Professional Insitute Inc. All rights reserved. Visit the Learning Materials website: https://learning.lpi.org
This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

Next Lesson

034.3 JavaScript Control Structures and Functions (034.3 Lesson 2)

Read next lesson

Linux Professional Insitute Inc. All rights reserved. Visit the Learning Materials website: https://learning.lpi.org
This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

LPI is a non-profit organization.

© 2023 Linux Professional Institute (LPI) is the global certification standard and career support organization for open source professionals. With more than 200,000 certification holders, it's the world’s first and largest vendor-neutral Linux and open source certification body. LPI has certified professionals in over 180 countries, delivers exams in multiple languages, and has hundreds of training partners.

Our purpose is to enable economic and creative opportunities for everybody by making open source knowledge and skills certification universally accessible.

  • LinkedIn
  • flogo-RGB-HEX-Blk-58 Facebook
  • Twitter
  • Contact Us
  • Privacy and Cookie Policy

Spot a mistake or want to help improve this page? Please let us know.

© 1999–2023 The Linux Professional Institute Inc. All rights reserved.