Control flow and error handling
JavaScript supports a compact set of statements, specificallycontrol flow statements, that you can use to incorporate a great deal of interactivityin your application. This chapter provides an overview of these statements.
TheJavaScript referencecontains exhaustive details about the statements in this chapter. The semicolon(;
) character is used to separate statements in JavaScript code.
Any JavaScript expression is also a statement.SeeExpressions and operatorsfor complete information about expressions.
Block statement
The most basic statement is ablock statement, which is used to groupstatements. The block is delimited by a pair of curly braces:
{ statement1; statement2; // … statementN;}
Example
Block statements are commonly used with control flow statements (if
,for
,while
).
while (x < 10) { x++;}
Here,{ x++; }
is the block statement.
Note:var
-declared variables are not block-scoped, but are scoped to the containing function or script, and the effects of setting them persist beyond the block itself. For example:
var x = 1;{ var x = 2;}console.log(x); // 2
This outputs2
because thevar x
statement within the block is in the same scope as thevar x
statement before the block. (In C or Java, the equivalent code would have output1
.)
Conditional statements
A conditional statement is a set of commands that executes if a specified condition istrue. JavaScript supports two conditional statements:if...else
andswitch
.
if...else statement
Use theif
statement to execute a statement if a logical condition istrue
. Use the optionalelse
clause to execute a statement ifthe condition isfalse
.
Anif
statement looks like this:
if (condition) { statement1;} else { statement2;}
Here, thecondition
can be any expression that evaluates totrue
orfalse
. (SeeBooleanfor an explanation of what evaluates totrue
andfalse
.)
Ifcondition
evaluates totrue
,statement1
is executed. Otherwise,statement2
is executed.statement1
andstatement2
can be any statement, including further nestedif
statements.
You can also compound the statements usingelse if
to have multipleconditions tested in sequence, as follows:
if (condition1) { statement1;} else if (condition2) { statement2;} else if (conditionN) { statementN;} else { statementLast;}
In the case of multiple conditions, only the first logical condition which evaluates totrue
will be executed. To execute multiple statements, group them within ablock statement ({ /* … */ }
).
Best practice
In general, it's good practice to always use block statements—especially whennestingif
statements:
if (condition) { // Statements for when condition is true // …} else { // Statements for when condition is false // …}
In general it's good practice to not have anif...else
with an assignment likex = y
as a condition:
if (x = y) { // statements here}
However, in the rare case you find yourself wanting to do something like that, thewhile
documentation has aUsing an assignment as a condition section with guidance on a general best-practice syntax you should know about and follow.
Falsy values
The following values evaluate tofalse
(also known asFalsy values):
false
undefined
null
0
NaN
- the empty string (
""
)
All other values—including all objects—evaluate totrue
when passed to aconditional statement.
Note:Do not confuse the primitive boolean valuestrue
andfalse
with the true and false values of theBoolean
object!
For example:
const b = new Boolean(false);if (b) { // this condition evaluates to true}if (b == true) { // this condition evaluates to false}
Example
In the following example, the functioncheckData
returnstrue
if the number of characters in aText
object is three. Otherwise, itdisplays an alert and returnsfalse
.
function checkData() { if (document.form1.threeChar.value.length === 3) { return true; } alert( `Enter exactly three characters. ${document.form1.threeChar.value} is not valid.`, ); return false;}
switch statement
Aswitch
statement allows a program to evaluate an expression and attemptto match the expression's value to acase
label. If a match is found, theprogram executes the associated statement.
Aswitch
statement looks like this:
switch (expression) { case label1: statements1; break; case label2: statements2; break; // … default: statementsDefault;}
JavaScript evaluates the above switch statement as follows:
- The program first looks for a
case
clause with a label matching thevalue of expression and then transfers control to that clause, executing theassociated statements. - If no matching label is found, the program looks for the optional
default
clause:- If a
default
clause is found, the program transfers control to thatclause, executing the associated statements. - If no
default
clause is found, the program resumes execution at thestatement following the end ofswitch
. - (By convention, the
default
clause is written as the last clause,but it does not need to be so.)
- If a
break statements
The optionalbreak
statement associated with eachcase
clauseensures that the program breaks out ofswitch
once the matched statement isexecuted, and then continues execution at the statement followingswitch
.Ifbreak
is omitted, the program continues execution inside theswitch
statement (and will execute statements under the nextcase
, and so on).
Example
In the following example, iffruitType
evaluates to"Bananas"
, the program matches the value withcase "Bananas"
and executes the associated statement. Whenbreak
is encountered, theprogram exits theswitch
and continues execution from the statementfollowingswitch
. Ifbreak
were omitted, the statement forcase "Cherries"
would also be executed.
switch (fruitType) { case "Oranges": console.log("Oranges are $0.59 a pound."); break; case "Apples": console.log("Apples are $0.32 a pound."); break; case "Bananas": console.log("Bananas are $0.48 a pound."); break; case "Cherries": console.log("Cherries are $3.00 a pound."); break; case "Mangoes": console.log("Mangoes are $0.56 a pound."); break; case "Papayas": console.log("Papayas are $2.79 a pound."); break; default: console.log(`Sorry, we are out of ${fruitType}.`);}console.log("Is there anything else you'd like?");
Exception handling statements
You can throw exceptions using thethrow
statement and handle them usingthetry...catch
statements.
Exception types
Just about any object can be thrown in JavaScript. Nevertheless, not all thrown objectsare created equal. While it is common to throw numbers or strings as errors, it isfrequently more effective to use one of the exception types specifically created forthis purpose:
throw statement
Use thethrow
statement to throw an exception. Athrow
statement specifies the value to be thrown:
throw expression;
You may throw any expression, not just expressions of a specific type. The followingcode throws several exceptions of varying types:
throw "Error2"; // String typethrow 42; // Number typethrow true; // Boolean typethrow { toString() { return "I'm an object!"; },};
try...catch statement
Thetry...catch
statement marks a block of statements to try, andspecifies one or more responses should an exception be thrown. If an exception isthrown, thetry...catch
statement catches it.
Thetry...catch
statement consists of atry
block, whichcontains one or more statements, and acatch
block, containing statementsthat specify what to do if an exception is thrown in thetry
block.
In other words, you want thetry
block to succeed—but if it does not, youwant control to pass to thecatch
block. If any statement within thetry
block (or in a function called from within thetry
block)throws an exception, controlimmediately shifts to thecatch
block. If no exception is thrown in thetry
block, thecatch
block is skipped. Thefinally
block executes after thetry
andcatch
blocks execute but before the statements following thetry...catch
statement.
The following example uses atry...catch
statement. The example calls afunction that retrieves a month name from an array based on the value passed to thefunction. If the value does not correspond to a month number(1
–12
), an exception is thrown with the value'InvalidMonthNo'
and the statements in thecatch
block set themonthName
variable to'unknown'
.
function getMonthName(mo) { mo--; // Adjust month number for array index (so that 0 = Jan, 11 = Dec) // prettier-ignore const months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ]; if (!months[mo]) { throw new Error("Invalid month code"); // throw keyword is used here } return months[mo];}try { // statements to try monthName = getMonthName(myMonth); // function could throw exception} catch (e) { monthName = "unknown"; logMyErrors(e); // pass exception object to error handler (i.e. your own function)}
The catch block
You can use acatch
block to handle all exceptions that may be generatedin thetry
block.
catch (exception) { statements}
Thecatch
block specifies an identifier (exception
in the preceding syntax) that holds the value specified by thethrow
statement. You can use this identifier to get information about the exception that wasthrown.
JavaScript creates this identifier when thecatch
block is entered. Theidentifier lasts only for the duration of thecatch
block. Once thecatch
block finishes executing, the identifier no longer exists.
For example, the following code throws an exception. When the exception occurs, controltransfers to thecatch
block.
try { throw "myException"; // generates an exception} catch (err) { // statements to handle any exceptions logMyErrors(err); // pass exception object to error handler}
Note:When logging errors to the console insideacatch
block, usingconsole.error()
rather thanconsole.log()
is advised for debugging. It formats the message as anerror, and adds it to the list of error messages generated by the page.
The finally block
Thefinally
block contains statements to be executedafter thetry
andcatch
blocks execute. Additionally, thefinally
block executesbefore the code that follows thetry…catch…finally
statement.
It is also important to note that thefinally
block will executewhether or not an exception is thrown. If an exception is thrown, however, thestatements in thefinally
block execute even if nocatch
blockhandles the exception that was thrown.
You can use thefinally
block to make your script fail gracefully when anexception occurs. For example, you may need to release a resource that your script hastied up.
The following example opens a file and then executes statements that use the file.(Server-side JavaScript allows you to access files.) If an exception is thrown while thefile is open, thefinally
block closes the file before the script fails.Usingfinally
hereensures that the file is never left open, evenif an error occurs.
openMyFile();try { writeMyFile(theData); // This may throw an error} catch (e) { handleError(e); // If an error occurred, handle it} finally { closeMyFile(); // Always close the resource}
If thefinally
block returns a value, this value becomes the return valueof the entiretry…catch…finally
production, regardless of anyreturn
statements in thetry
andcatch
blocks:
function f() { try { console.log(0); throw "bogus"; } catch (e) { console.log(1); // This return statement is suspended // until finally block has completed return true; console.log(2); // not reachable } finally { console.log(3); return false; // overwrites the previous "return" console.log(4); // not reachable } // "return false" is executed now console.log(5); // not reachable}console.log(f()); // 0, 1, 3, false
Overwriting of return values by thefinally
block also applies toexceptions thrown or re-thrown inside of thecatch
block:
function f() { try { throw "bogus"; } catch (e) { console.log('caught inner "bogus"'); // This throw statement is suspended until // finally block has completed throw e; } finally { return false; // overwrites the previous "throw" } // "return false" is executed now}try { console.log(f());} catch (e) { // this is never reached! // while f() executes, the `finally` block returns false, // which overwrites the `throw` inside the above `catch` console.log('caught outer "bogus"');}// Logs:// caught inner "bogus"// false
Nesting try...catch statements
You can nest one or moretry...catch
statements.
If an innertry
block doesnot have a correspondingcatch
block:
- itmust contain a
finally
block, and - the enclosing
try...catch
statement'scatch
block ischecked for a match.
For more information, seenested try-blockson thetry...catch
reference page.
Utilizing Error objects
Depending on the type of error, you may be able to use thename
andmessage
properties to get a more refined message.
Thename
property provides the general class ofError
(suchasDOMException
orError
), whilemessage
generally provides a more succinct message than one would get by converting the errorobject to a string.
If you are throwing your own exceptions, in order to take advantage of these properties(such as if yourcatch
block doesn't discriminate between your ownexceptions and system ones), you can use theError
constructor.
For example:
function doSomethingErrorProne() { if (ourCodeMakesAMistake()) { throw new Error("The message"); } doSomethingToGetAJavaScriptError();}try { doSomethingErrorProne();} catch (e) { // Now, we actually use `console.error()` console.error(e.name); // 'Error' console.error(e.message); // 'The message', or a JavaScript error message}