Jump to content

JavaScript/Control structures

From Wikibooks, open books for an open world



Most programming languages are composed of 'bricks' like tokens (keywords, variables, operators, ...), expressions like myArray.length + 1, statements (delimited by ;), blocks {...}, functions, and modules. At first glance, the execution of the program follows the sequence of statements, top down. But in nearly all cases, it is necessary that the program does not run in the strict order of the written statements. Instead, some parts must run only if certain conditions apply, and others will be omitted and run under different conditions. Or, it may become necessary that some parts are executed repetitively. Other parts may run in parallel and get synchronized later. Or, a function of a different module must compute a value before the next statement can be executed.

In this hierarchy of 'language bricks' the term block is essential for the understanding of the program flow. In JavaScript, a block is a sequence of zero or more statements (or smaller blocks) that are surrounded by braces { // zero or more statements }. The language constructions we discuss here invoke or repeat blocks.

if / else

[edit | edit source]

The if / else statement (yes, it's a single statement, even though it contains other statements in its blocks) invokes the execution of one of two blocks depending on the evaluation of a condition. The evaluation returns a boolean value. If it is true, the first block is executed; if it is false, the second block is executed. The respectively other block is skipped over.

if ( condition ) {
  // block of statements
} else {
  // block of statements
}

The else part is optional, i.e. it's possible to use if without the else part and its block.

if ( condition ) {
  // block of statements
}

An example:

"use strict";
const a = 3;
const b = "3";
if (a == b) {
  alert("The two variables contain the same value, but may have different data types.");
} else {
  alert("The two variables contain different values.");
}

// an example without 'else'
const c = 6 / 2;
if (a === c) {
  alert("The two variables contains the same value and are of the same data type.");
}

If one of the two blocks contains exactly ONE statement, the braces can be omitted. But for the clearness of the code, we recommend the use of a unified syntax with braces.

// same as above; but this abbreviated syntax is not recommended.

"use strict";
const a = 3;
const b = "3";

if (a == b) alert("The two variables contains the same value, but may have different data types.");
else alert("The two variables contain different values.");

const c = 6 / 2;
if (a === c) alert("The two variables contains the same value and are of the same data type.");

In many cases, the situation demands more complex decisions than a simple true/false alternative. For example, you may want to know whether a number is negative, zero, or positive. In such cases, a solution might look like this:

"use strict";
const x = 3;

if (x < 0) {
  alert("The number is negative.");
} else {
  // x is equal or greater than 0
  if (x === 0) {
    alert("The number zero.");
  } else {
    alert("The number is positive.");
  }
}

You can shorten this code a bit without losing clarity. Because the first else block contains only a single statement - namely the second if - you can omit its braces and combine the first else and the second if within one line.

"use strict";
const x = 3;

if (x < 0) {
  alert("The number is negative.");
} else if (x === 0) {
  alert("The number is zero.");
} else {
  alert("The number is positive.");
}

This is a clear and often-used programming style. It's used in situations where you have a manageable number of choices or where you have to make decisions with multiple variables.

switch

[edit | edit source]

If the number of decisions grows significantly, the code gets clearer if you use the switch statement instead of a long list of else if conditions.

The switch statement evaluates an expression and steers the flow of statements based on the comparison of its result with the labels behind the keyword case.

"use strict";

const myVar = "a";

// evaluation takes simple variables as well as complex expressions
switch (myVar.toUpperCase()) {
case "A":
  // …
  break;
case "B":
  // …
  break;
default:   // analog to 'else' without any further 'if'
  // …
  break;
}

If the result of the evaluation matches one of the labels, JavaScript executes the following statements up to the next break or the end of the entire switch. If none of the labels match, execution continues at the default label, or - if none is present - skips the switch statement entirely.

Labels are literals or expressions; e.g., case (2 + 1).toString(): is possible.

As soon as a break statement is reached, the execution of the switch gets terminated. Normally it appears at the end of each case to prevent execution of the code of the following cases. But it can be omitted if you intentionally want to execute them in addition to the current ones. In the following example, the same code will run for i equal to 1, 2, or 3.

"use strict";

const i = 2;

switch(i) {
case 1:
case 2:
case 3:
  // …
  break;
case 4:
  // …
  break;
default:
  // …
  break;
}

Because the expression to be evaluated as well as the labels can be complex expressions, it's possible to build very flexible constructions.

"use strict";

const i = 2;

switch(true) { // in this example it's a constant value
case (i < 10):
  alert("one digit");
  break;
case (i >= 10 && i < 100):
  alert("two digits");
  break;
default:
  // …
  break;
}


The continue keyword does not apply to the switch statement.

try / catch / finally

[edit | edit source]

If there is a possibility that a runtime error might occur, you can 'catch' that error and perform meaningful actions to handle the situation. E.g., a network connection or a database might no longer be available; a user input leads to a division by zero; ... .

try {
  // critical block where errors might occur
} catch (err) {
  // block to handle possible errors. Normally not executed.
} finally {
  // block that will be executed in ALL cases
}
"use strict";

const x = 15;
let average;
try {
  // block with critical statements
  x = x + 5;
  average = x / 0;
  alert("The average is: " + average);
} catch (err) {
  // block to handle possible errors
  alert("Something strange occurs. The error is: " + err);
} finally {
  // block that will be executed in ALL cases
  alert("End of program.");
}

If one of the statements in the critical block raises a runtime error, the execution of its remaining statements is omitted. Instead, the execution invokes the catch block. Lastly, the finally block is executed.

Please note that the finally block is executed in all cases, regardless of whether a runtime error occurs or not. That even applies if the critical or the catch block executes a return statement.

throw

[edit | edit source]

In the above example, the JavaScript engine throws an exception by itself. In other situations, the JavaScript engine acts in one way or another, but you may want to see it treated differently. E.g., in the case of a division by zero, the engine doesn't throw an error; it assigns Infinity to the result and jumps to the following statement. If you want a different behavior, you can create and throw exceptions by your own program.

"use strict";

const x = 15;
let average;
try {
  // block with critical statements
  average = x / 0;
  // or: const z = "abc"; average = z / 0;
  if (average === Infinity || Number.isNaN(average)) {
    // Throw your own exception with any text
    throw "Error during division. The result is: " + average;
  }
  alert("The average is: " + average);
} catch (err) {
  // block to handle possible errors
  alert("Something strange occurs. The error is: " + err);
} finally {
  // block that will be executed in ALL cases
  alert("End of program.");
}

If an exception occurs - generated by the JavaScript engine or by your program - and is not caught by a catch block, the script terminates or - if it is a function - it returns control to the calling function. The error handling may be implemented there or in one of the functions which have been called it.

"use strict";

const answer = prompt("How old are you?");
const age = Number(answer);

if (isNaN(age)) {
  throw answer + " cannot be converted to a number.";
  // The script terminates with this message (it's not a function)
}
alert("Next year you will be " + (age + 1));

Exercises

[edit | edit source]
... are available on another page (click here).

Loops

[edit | edit source]

Loops and iterations are other cases where the sequential flow of statements is manipulated by surrounding language constructs. This is described on the next page.

See also

[edit | edit source]