JavaScript – Jumps

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
    specified expression at the beginning
    of the loop is tested again, and if it’s true, 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 the test 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.

Comments are closed.