Another category of JavaScript statements are jump
statements. As the name implies, these cause the JavaScript
interpreter to jump to a new location in the source code. The break
statement makes the interpreter jump
to the end of a loop or other statement. continue
makes the interpreter skip the rest
of the body of a loop and jump back to the top of a loop to begin a
new iteration. JavaScript allows statements to be named, or
labeled, and the break
and continue
can identify the target loop or
other statement label.
The return
statement makes
the interpreter jump from a function invocation back to the code that
invoked it and also supplies the value for the invocation. The
throw
statement raises, or
“throws,” an exception and is designed to work with the try/catch/finally
statement, which
establishes a block of exception handling code. This is a complicated
kind of jump statement: when an exception is thrown, the interpreter
jumps to the nearest enclosing exception handler, which may be in the
same function or up the call stack in an invoking function.
Details of each of these jump statements are in the sections
that follow.
Labeled Statements
Any statement may be labeled by preceding
it with an identifier and a colon:
identifier: statement
By labeling a statement, you give it a name that you can use
to refer to it elsewhere in your program. You can label any
statement, although it is only useful to label statements that have
bodies, such as loops and conditionals. By giving a loop a name, you
can use break
and continue
statements inside the body of the
loop to exit the loop or to jump directly to the top of the loop to
begin the next iteration. break
and continue
are the only
JavaScript statements that use statement labels; they are covered
later in this chapter. Here is an example of a labeled while
loop and a continue
statement that uses the
label.
mainloop: while(token != null) {
// Code omitted...
continue mainloop; // Jump to the next iteration of the named loop
// More code omitted...
}
The identifier
you use to label a
statement can be any legal JavaScript identifier that is not a
reserved word. The namespace for labels is different than the
namespace for variables and functions, so you can use the same
identifier as a statement label and as a variable or function name.
Statement labels are defined only within the statement to which they
apply (and within its substatements, of course). A statement may not
have the same label as a statement that contains it, but two
statements may have the same label as long as neither one is nested
within the other. Labeled statements may themselves be labeled.
Effectively, this means that any statement may have multiple
labels.
break
The break
statement, used
alone, causes the innermost enclosing loop or switch
statement to exit immediately. Its
syntax is simple:
break;
Because it causes a loop or switch
to exit, this form of the break
statement is legal only if it
appears inside one of these statements.
You’ve already seen examples of the break
statement within a switch
statement. In loops, it is
typically used to exit prematurely when, for whatever reason, there
is no longer any need to complete the loop. When a loop has complex
termination conditions, it is often easier to implement some of
these conditions with break
statements rather than trying to express them all in a single loop
expression. The following code searches the elements of an array for
a particular value. The loop terminates in the normal way when it
reaches the end of the array; it terminates with a break
statement if it finds what it is
looking for in the array:
for(var i = 0; i < a.length; i++) {
if (a[i] == target) break;
}
JavaScript also allows the break
keyword to be followed by a
statement label (just the identifier, with no colon):
break labelname;
When break
is used with a
label, it jumps to the end of, or terminates, the enclosing
statement that has the specified label. It is a syntax error to use
break
in this form if there is no
enclosing statement with the specified label. With this form of the
break
statement, the named
statement need not be a loop or switch
: break
can “break out of” any enclosing
statement. This statement can even be a statement block grouped
within curly braces for the sole purpose of naming the block with a
label.
A newline is not allowed between the break
keyword and the
labelname
. This is a result of
JavaScript’s automatic insertion of omitted semicolons: if you put a
line terminator between the break
keyword and the label that follows, JavaScript assumes you meant to
use the simple, unlabeled form of the statement and treats the line
terminator as a semicolon. (See Optional Semicolons.)
You need the labeled form of the break
statement when you want to break out
of a statement that is not the nearest enclosing loop or a switch.
The following code demonstrates:
var matrix = getData(); // Get a 2D array of numbers from somewhere
// Now sum all the numbers in the matrix.
var sum = 0, success = false;
// Start with a labeled statement that we can break out of if errors occur
compute_sum: if (matrix) {
for(var x = 0; x < matrix.length; x++) {
var row = matrix[x];
if (!row) break compute_sum;
for(var y = 0; y < row.length; y++) {
var cell = row[y];
if (isNaN(cell)) break compute_sum;
sum += cell;
}
}
success = true;
}
// The break statements jump here. If we arrive here with success == false
// then there was something wrong with the matrix we were given.
// Otherwise sum contains the sum of all cells of the matrix.
Finally, note that a break
statement, with or without a label, can not transfer control across
function boundaries. You cannot label a function definition
statement, for example, and then use that label inside the
function.
continue
The continue
statement is
similar to the break
statement.
Instead of exiting a loop, however, continue
restarts a loop at the next
iteration. The continue
statement’s syntax is just as simple as the break
statement’s:
continue;
The continue
statement can
also be used with a label:
continue labelname;
The continue
statement, in
both its labeled and unlabeled forms, can be used only within the
body of a loop. Using it anywhere else causes a syntax
error.
When the continue
statement
is executed, the current iteration of the enclosing loop is
terminated, and the next iteration begins. This means different
things for different types of loops:
-
In a
while
loop, the
specifiedexpression
at the beginning
of the loop is tested again, and if it’strue
, the loop body is executed
starting from the top. -
In a
do/while
loop,
execution skips to the bottom of the loop, where the loop
condition is tested again before restarting the loop at the
top. -
In a
for
loop, the
increment
expression is evaluated,
and thetest
expression is tested
again to determine if another iteration should be
done. -
In a
for/in
loop, the
loop starts over with the next property name being assigned to
the specified variable.
Note the difference in behavior of the continue
statement in the while
and for
loops: a while
loop returns directly to its
condition, but a for
loop first
evaluates its increment
expression and then
returns to its condition. Earlier we considered the behavior of the
for
loop in terms of an
“equivalent” while
loop. Because
the continue
statement behaves
differently for these two loops, however, it is not actually
possible to perfectly simulate a for
loop with a while
loop alone.
The following example shows an unlabeled continue
statement being used to skip the
rest of the current iteration of a loop when an error occurs:
for(i = 0; i < data.length; i++) {
if (!data[i]) continue; // Can't proceed with undefined data
total += data[i];
}
Like the break
statement,
the continue
statement can be
used in its labeled form within nested loops, when the loop to be
restarted is not the immediately enclosing loop. Also, like the
break
statement, line breaks are
not allowed between the continue
statement and its labelname
.
return
Recall that function invocations are expressions and that all
expressions have values. A return
statement within a function specifies the value of invocations of
that function. Here’s the syntax of the return
statement:
return expression;
A return
statement may
appear only within the body of a function. It is a syntax error for
it to appear anywhere else. When the return
statement is executed, the function
that contains it returns the value of
expression
to its caller. For
example:
function square(x) { return x*x; } // A function that has a return statement
square(2) // This invocation evaluates to 4
With no return
statement, a
function invocation simply executes each of the statements in the
function body in turn until it reaches the end of the function, and
then returns to its caller. In this case, the invocation expression
evaluates to undefined
. The
return
statement often appears as
the last statement in a function, but it need not be last: a
function returns to its caller when a return
statement is executed, even if
there are other statements remaining in the function body.
The return
statement can
also be used without an expression
to
make the function return undefined
to its caller. For
example:
function display_object(o) {
// Return immediately if the argument is null or undefined.
if (!o) return;
// Rest of function goes here...
}
Because of JavaScript’s automatic semicolon insertion (Optional Semicolons), you cannot include a line break
between the return
keyword and
the expression that follows it.
throw
An exception is a signal that indicates
that some sort of exceptional condition or error has occurred. To
throw an exception is to signal such an error
or exceptional condition. To catch an exception
is to handle it—to take whatever actions are necessary or appropriate to recover from the
exception. In JavaScript, exceptions are thrown whenever a runtime
error occurs and whenever the program explicitly throws one using
the throw
statement. Exceptions
are caught with the try/catch/finally
statement, which is
described in the next section.
The throw
statement has the
following syntax:
throw expression;
expression
may evaluate to a value
of any type. You might throw a number that represents an error code
or a string that contains a human-readable error message. The Error
class and its subclasses are used when the JavaScript interpreter
itself throws an error, and you can use them as well. An Error
object has a name
property that
specifies the type of error and a message
property that holds the string
passed to the constructor function (see the Error class in the
reference section). Here is an example function that throws an Error
object when invoked with an invalid argument:
function factorial(x) {
// If the input argument is invalid, throw an exception!
if (x < 0) throw new Error("x must not be negative");
// Otherwise, compute a value and return normally
for(var f = 1; x > 1; f *= x, x--) /* empty */ ;
return f;
}
When an exception is thrown, the JavaScript interpreter
immediately stops normal program execution and jumps to the nearest
exception handler. Exception handlers are written using the catch
clause of the try/catch/finally
statement, which is
described in the next section. If the block of code in which the
exception was thrown does not have an associated catch
clause, the interpreter checks the
next highest enclosing block of code to see if it has an exception
handler associated with it. This continues until a handler is found.
If an exception is thrown in a function that does not contain a
try/catch/finally
statement to
handle it, the exception propagates up to the code that invoked the
function. In this way, exceptions propagate up through the lexical
structure of JavaScript methods and up the call stack. If no
exception handler is ever found, the exception is treated as an
error and is reported to the user.
try/catch/finally
The try/catch/finally
statement is JavaScript’s exception handling mechanism. The try
clause of this statement simply
defines the block of code whose exceptions are to be handled. The
try
block is followed by a
catch
clause, which is a block of
statements that are invoked when an exception occurs anywhere within
the try
block. The catch
clause is followed by a finally
block containing cleanup code that
is guaranteed to be executed, regardless of what happens in the
try
block. Both the catch
and finally
blocks are optional, but a
try
block must be accompanied by
at least one of these blocks. The try
, catch
, and finally
blocks all begin and end with
curly braces. These braces are a required part of the syntax and
cannot be omitted, even if a clause contains only a single
statement.
The following code illustrates the syntax and purpose of the
try/catch/finally
statement:
try {
// Normally, this code runs from the top of the block to the bottom
// without problems. But it can sometimes throw an exception,
// either directly, with a throw statement, or indirectly, by calling
// a method that throws an exception.
}
catch (e) {
// The statements in this block are executed if, and only if, the try
// block throws an exception. These statements can use the local variable
// e to refer to the Error object or other value that was thrown.
// This block may handle the exception somehow, may ignore the
// exception by doing nothing, or may rethrow the exception with throw.
}
finally {
// This block contains statements that are always executed, regardless of
// what happens in the try block. They are executed whether the try
// block terminates:
// 1) normally, after reaching the bottom of the block
// 2) because of a break, continue, or return statement
// 3) with an exception that is handled by a catch clause above
// 4) with an uncaught exception that is still propagating
}
Note that the catch
keyword
is followed by an identifier in parentheses. This identifier is like
a function parameter. When an exception is caught, the value
associated with the exception (an Error object, for example) is
assigned to this parameter. Unlike regular variables, the identifier
associated with a catch
clause
has block scope—it is only defined within the catch
block.
Here is a realistic example of the try/catch
statement. It uses the factorial()
method defined in the previous
section and the client-side JavaScript methods prompt()
and alert()
for input and output:
try {
// Ask the user to enter a number
var n = Number(prompt("Please enter a positive integer", ""));
// Compute the factorial of the number, assuming the input is valid
var f = factorial(n);
// Display the result
alert(n + "! = " + f);
}
catch (ex) { // If the user's input was not valid, we end up here
alert(ex); // Tell the user what the error is
}
This example is a try/catch
statement with no finally
clause.
Although finally
is not used as
often as catch
, it can be useful.
However, its behavior requires additional explanation. The finally
clause is guaranteed to be
executed if any portion of the try
block is executed, regardless of how
the code in the try
block
completes. It is generally used to clean up after the code in the
try
clause.
In the normal case, the JavaScript interpreter reaches the end
of the try
block and then
proceeds to the finally
block,
which performs any necessary cleanup. If the interpreter left the
try
block because of a return
, continue
, or break
statement, the finally
block is executed before the
interpreter jumps to its new destination.
If an exception occurs in the try
block and there is an associated
catch
block to handle the
exception, the interpreter first executes the catch
block and then the finally
block. If there is no local
catch
block to handle the
exception, the interpreter first executes the finally
block and then jumps to the
nearest containing catch
clause.
If a finally
block itself
causes a jump with a return
,
continue
, break
, or throw
statement, or by calling a method
that throws an exception, the interpreter abandons whatever jump was
pending and performs the new jump. For example, if a finally
clause throws an exception, that
exception replaces any exception that was in the process of being
thrown. If a finally
clause
issues a return
statement, the
method returns normally, even if an exception has been thrown and
has not yet been handled.
try
and finally
can be used together without a
catch
clause. In this case, the
finally
block is simply cleanup
code that is guaranteed to be executed, regardless of what happens
in the try
block. Recall that we
can’t completely simulate a for
loop with a while
loop because
the continue
statement behaves
differently for the two loops. If we add a try/finally
statement, we can write a
while
loop that works like a
for
loop and that handles
continue
statements
correctly:
// Simulate for( initialize ; test ; increment ) body;
initialize ;
while( test ) {
try { body ; }
finally { increment ; }
}
Note, however, that a body
that
contains a break
statement
behaves slightly differently (causing an extra increment before
exiting) in the while
loop than
it does in the for
loop, so even
with the finally
clause, it is
not possible to completely simulate the for
loop with while
.