JavaScript – Constants and Scoped Variables

We now leave language subsets behind and transition to language
extensions. In JavaScript 1.5 and later, you can use the const keyword to define constants. Constants
are like variables except that assignments to them are ignored
(attempting to alter a constant does not cause an error) and attempts
to redeclare them cause errors:

const pi = 3.14;  // Define a constant and give it a value.
pi = 4;           // Any future assignments to it are silently ignored.
const pi = 4;     // It is an error to redeclare a constant.
var pi = 4;       // This is also an error.

The const keyword behaves
much like the var keyword: there is
no block scope, and constants are hoisted to the
top of the enclosing function definition. (See Function Scope and Hoisting)

The lack of block scope for variables in JavaScript has long
been considered a shortcoming of the language, and JavaScript 1.7
addresses it by adding the let
keyword to the language. The keyword const has always been a reserved (but
unused) word in JavaScript, so constants can be added without breaking
any existing code. The let keyword
was not reserved, so it is not recognized unless you explicitly opt-in
to version 1.7 or later.

JavaScript Versions

In this chapter, when we refer to a specific JavaScript
version number, we’re referring specifically to Mozilla’s version of
the language, as implemented in the Spidermonkey and Rhino
interpreters and the Firefox web browser.

Some of the language extensions here define new keywords (such
as let) and to avoid breaking
existing code that uses that keyword, JavaScript requires you to
explicitly request the new version of the language in order to use
the extension. If you are using Spidermonkey or Rhino as a
stand-alone interpreter, you can specify the desired language version with a command-line
option or by calling the built-in version() function. (It expects the
version number times ten. Pass 170 to select JavaScript 1.7 and
enable the let keyword.) In
Firefox, you can opt in to language extensions using a script tag
like this:

<script type="application/javascript; version=1.8">

The let keyword can be used
in four ways:

  • as a variable declaration like var;

  • in a for or for/in loop, as a substitute for
    var;

  • as a block statement, to define new variables and explicitly
    delimit their scope; and

  • to define variables that are scoped to a single
    expression.

The simplest way to use let
is as a drop-in replacement for var. Variables declared with var are defined throughout the enclosing
function. Variables declared with let are defined only within the closest
enclosing block (and any blocks nested within it, of course). If you
declare a variable with let inside
the body of a loop, for example, it does not exist outside the
loop:

function oddsums(n) {
    let total = 0, result=[];        // Defined throughout the function
    for(let x = 1; x <= n; x++) {    // x is only defined in the loop
        let odd = 2*x-1;             // odd only defined in this loop
        total += odd;
        result.push(total);
    }
    // Using x or odd here would cause a ReferenceError
    return result;
}

oddsums(5);  // Returns [1,4,9,16,25]

Notice that this code also uses let as a replacement for var in the for loop. This creates a variable whose
scope is the body of the loop plus the condition and increment clauses
of the loop. You can also use let
in this way in for/in (and for each; see The for/each Loop) loops:

o = {x:1,y:2};
for(let p in o) console.log(p);      // Prints x and y
for each(let v in o) console.log(v); // Prints 1 and 2
console.log(p)                       // ReferenceError: p is not defined

There is an interesting difference between let used as a declaration statement and
let used as a loop initializer.
Used as a declaration, the variable initializer expressions are
evaluated in the scope of the variable. But in a for loop, the initializer expression is
evaluated outside the scope of the new variable. This matters only
when the new variable is shadowing a new variable by the same
name:

let x = 1;
for(let x = x + 1; x < 5; x++) 
    console.log(x); // Prints 2,3,4

{                   // Begin a block to create a new variable scope
    let x = x + 1;  // x is undefined, so x+1 is NaN
    console.log(x); // Prints NaN
}

Variables declared with var
exist throughout the function in which they are declared, but they are
not initialized until the var
statement actually runs. That is, the variable exists (i.e., no
ReferenceError will be thrown) but
is undefined if you attempt to use
it before the var statement.
Variables declared with let are
similar: if you attempt to use a variable before its let statement (but within the same block as
the let statement), the variable
will exist but its value will be undefined.

Notice that this problem doesn’t exist when you use let to declare a loop variable—the syntax
simply doesn’t allow you to use the variable before it is initialized.
There is another way to use let
that avoids this problem of using variables before they are
initialized. A let block statement
(as opposed to the let declaration
statements shown above) combines a block of code with a set of
variables for the block and the initialization expressions for those
variables. In this form, the variables and their initializers are
placed within parentheses and are followed by a block of statements
within curly braces:

let x=1, y=2;
let (x=x+1,y=x+2) {   // Note that we're shadowing variables
    console.log(x+y); // Prints 5
};
console.log(x+y);     // Prints 3

It is important to understand that the variable initializer
expressions of a let block are not
part of the block and are interpreted in the outer scope. In the code
above, we are creating a new variable x and assigning it a value one
larger than the value of the existing variable x.

The final use of the let
keyword is a variant on the let
block, in which a parenthesized list of variables and initializers is
followed by a single expression rather than a block of statements.
This is called a let expression,
and the code above could be rewritten to use one like this:

let x=1, y=2;
console.log(let (x=x+1,y=x+2) x+y);  // Prints 5

Some form of const and
let (not necessarily all four forms
described here) are likely to be included in a future version of the
ECMAScript standard.

Comments are closed.