New in JavaScript 1.7

  • Revision slug: JavaScript/New_in_JavaScript/1.7
  • Revision title: New in JavaScript 1.7
  • Revision id: 51912
  • Created:
  • Creator: Sheppy
  • Is current revision? No
  • Comment no wording changes

Revision Content

{{ Fx_minversion_header("2") }} JavaScript 1.7 is a language update introducing several new features, in particular generators, iterators, array comprehensions, let expressions, and destructuring assignment. It also includes all the features of JavaScript 1.6.

JavaScript 1.7 support was introduced in Firefox 2.

The code samples included in this article can be experimented with in the JavaScript shell. Read Introduction to the JavaScript shell to learn how to build and use the shell.

Using JavaScript 1.7

In order to use some of the new features of JavaScript 1.7, you need to specify that you wish to use JavaScript 1.7. In HTML or XUL code, use:

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

When using the JavaScript shell, you need to set the version you wish to use using the -version 170 switch on the command line or using the version() function:

 version(170);

The features that require the use of the new keywords "yield" and "let" require you to specify version 1.7 because existing code might use those keywords as variable or function names. The features that do not introduce new keywords (destructuring assignment and array comprehensions) can be used without specifying the JavaScript version.

Generators and iterators

When developing code that involves an iterative algorithm (such as iterating over a list, or repeatedly performing computations on the same data set), there are often state variables whose values need to be maintained for the duration of the computation process. Traditionally, you have to use a callback function to obtain the intermediate values of an iterative algorithm.

Generators

Consider this iterative algorithm that computes Fibonacci numbers:

function do_callback(num) {
  document.write(num + "<br>\n");
}

function fib() {
  var i = 0, j = 1, n = 0;
  while (n < 10) {
    do_callback(i);
    var t = i;
    i = j;
    j += t;
    n++;
  }
}

fib();

This code uses a callback routine to perform operations on each iterative step of the algorithm. In this case, each Fibonacci number is simply printed to the console.

Generators and iterators work together to provide a new, better, way to do this. Let's see how the Fibonacci number routine looks written using a generator:

function fib() {
  var i = 0, j = 1;
  while (true) {
    yield i;
    var t = i;
    i = j;
    j += t;
  }
}

var g = fib();
for (var i = 0; i < 10; i++) {
  document.write(g.next() + "<br>\n");
}

The function containing the yield keyword is a generator. When you call it, its formal parameters are bound to actual arguments, but its body isn't actually evaluated. Instead, a generator-iterator is returned. Each call to the generator-iterator's next() method performs another pass through the iterative algorithm. Each step's value is the value specified by the yield keyword. Think of yield as the generator-iterator version of return, indicating the boundary between each iteration of the algorithm. Each time you call next(), the generator code resumes from the statement following the yield.

You cycle a generator-iterator by repeatedly calling its next() method until you reach your desired result condition. In this example, we can obtain however many Fibonacci numbers we want by continuing to call g.next() until we have the number of results we want.

Resuming a generator at a specific point

Once a generator has been started by calling its next() method, you can use send(), passing a specific value that will be treated as the result of the last yield. The generator will then return the operand of the subsequent yield.

You can't start a generator at an arbitrary point; you must start it with next() before you can send() it a specific value.

Note: As a point of interest, calling send(undefined) is equivalent to calling next(). However, starting a newborn generator with any value other than undefined when calling send() will result in a TypeError exception.
Exceptions in generators

You can force a generator to throw an exception by calling its throw() method, passing the exception value it should throw. This exception will be thrown from the current suspended context of the generator, as if the yield that is currently suspended were instead a throw value statement.

If a yield is not encountered during the processing of the thrown exception, then the exception will propagate up through the call to throw(), and subsequent calls to next() will result in StopIteration being thrown.

Closing a generator

Generators have a close() method that forces the generator to close itself. The effects of closing a generator are:

  1. Any finally clauses active in the generator function are run.
  2. If a finally clause throws any exception other than StopIteration, the exception is propagated to the caller of the close() method.
  3. The generator terminates.
Generator Example

This code drives a generator that will yield every 100 loops.

var gen = generator();

function driveGenerator() {
  if (gen.next()) {
    window.setTimeout(driveGenerator, 0);	
  } else {
    gen.close();	
  }
}

function generator() {
  while (i < something) {
    /** stuff **/

    ++i;
    /** 100 loops per yield **/
    if ((i % 100) == 0) {
      yield true;
    } 
  }
  yield false;
}

Iterators

An iterator is a special object that lets you iterate over data.

In normal usage, iterator objects are "invisible"; you won't need to operate on them explicitly, but will instead use JavaScript's for...in and for each...in statements to loop naturally over the keys and/or values of objects.

var objectWithIterator = getObjectSomehow();

for (var i in objectWithIterator)
{
  document.write(objectWithIterator[i] + "<br>\n");
}

If you are implementing your own iterator object, or have another need to directly manipulate iterators, you'll need to know about the next method, the StopIteration exception, and the __iterator__ method.

You can create an iterator for an object by calling Iterator(objectname); the iterator for an object is found by calling the object's __iterator__ method. If no __iterator__ method is present, a default iterator is created. The default iterator yields the object's properties, according to the usual for...in and for each...in model. If you wish to provide a custom iterator, you should override the __iterator__ method to return an instance of your custom iterator. To get an object's iterator from script, you should use Iterator(obj) rather than accessing the __iterator__ property directly. The former works for Arrays; the latter doesn't.

Once you have an iterator, you can easily fetch the next item in the object by calling the iterator's next() method. If there is no data left, the StopIteration exception is thrown.

Here's a simple example of direct iterator manipulation:

var obj = {name:"Jack Bauer", username:"JackB", id:12345, agency:"CTU",
          region:"Los Angeles"}; 

var it = Iterator(obj);

try {
  while (true) {
    print(it.next() + "\n");
  }
} catch (err if err instanceof StopIteration) {
  print("End of record.\n");
} catch (err) {
  print("Unknown error: " + err.description + "\n");
}

The output from this program looks like this:

name,Jack Bauer
username,JackB
id,12345
agency,CTU
region,Los Angeles
End of record.

You can, optionally, specify a second parameter when creating your iterator, which is a boolean value that indicates whether or not you only want the keys returned each time you call its next() method. This parameter is passed in to user-defined __iterator__ functions as its single argument. Changing var it = Iterator(obj); to var it = Iterator(obj, true); in the above sample results in the following output:

name
username
id
agency
region
End of record.

In both cases, the actual order in which the data is returned may vary based on the implementation. There is no guaranteed ordering of the data.

Iterators are a handy way to scan through the data in objects, including objects whose content may include data you're unaware of. This can be particularly useful if you need to preserve data your application isn't expecting.

Array comprehensions

Array comprehensions are a use of generators that provides a convenient way to perform powerful initialization of arrays. For example:

function range(begin, end) {
  for (let i = begin; i < end; ++i) {
    yield i;
  }
}

range() is a generator that returns all the values between begin and end. Having defined that, we can use it like this:

var ten_squares = [i * i for each (i in range(0, 10))];

This pre-initializes a new array, ten_squares, to contain the squares of the values in the range 0..9.

You can use any conditional when initializing the array. If you want to initialize an array to contain the even numbers between 0 and 20, you can use this code:

var evens = [i for each (i in range(0, 21)) if (i % 2 == 0)];

Prior to JavaScript 1.7, this would have to be coded something like this:

var evens = [];
for (var i=0; i <= 20; i++) {
  if (i % 2 == 0)
    evens.push(i);
}

Not only is the array comprehension much more compact, but it's actually easier to read, once you're familiar with the concept.

Scoping rules

Array comprehensions have an implicit block around them, containing everything inside the square brackets, as well as implicit let declarations.

Block scope with let

There are several ways in which let can be used to manage block scope of data and functions:

  • The let statement provides a way to associate values with variables within the scope of a block, without affecting the values of like-named variables outside the block.
  • The let expression lets you establish variables scoped only to a single expression.
  • The let definition defines variables whose scope is constrained to the block in which they're defined. This syntax is very much like the syntax used for var.
  • You can also use let to establish variables that exist only within the context of a for loop.

let statement

The let statement provides local scoping for variables. It works by binding zero or more variables in the lexical scope of a single block of code; otherwise, it is exactly the same as a block statement. Note in particular that the scope of a variable declared inside a let statement using var is still the same as if it had been declared outside the let statement; such variables still have function scoping.

For example:

var x = 5;
var y = 0;

let (x = x+10, y = 12) {
  print(x+y + "\n");
}

print((x + y) + "\n");

The output from this program will be:

27
5

The rules for the code block are the same as for any other code block in JavaScript. It may have its own local variables established using the let declarations.

Note: When using the let statement syntax, the parentheses following let are required. Failure to include them will result in a syntax error.
Scoping rules

The scope of variables defined using let is the let block itself, as well as any inner blocks contained inside it, unless those blocks define variables by the same names.

let expressions

You can use let to establish variables that are scoped only to a single expression:

var x = 5;
var y = 0;
document.write( let(x = x + 10, y = 12) x+y  + "<br>\n");
document.write(x+y + "<br>\n");

The resulting output is:

27
5

In this case, the binding of the values of x and y to x+10 and 12 are scoped solely to the expression x+y.

Scoping rules

Given a let expression:

let (decls) expr

There is an implicit block created around expr.

let definitions

The let keyword can also be used to define variables inside a block.

Note: If you have more interesting examples of ways to use let definitions, please consider adding them here.
if (x > y)
{
   let gamma = 12.7 + y;
   i = gamma * x;
}

let statements, expressions and definitions sometimes make the code cleaner when inner functions are used.

var list = document.getElementById("list");
for (var i = 1; i <= 5; i++)
{
  var item = document.createElement("LI");
  item.appendChild(document.createTextNode("Item " + i));
  let j = i;
  item.onclick = function (ev) {
    alert("Item " + j + " is clicked.");
  };
  list.appendChild(item);
}

The example above works as intended because the five instances of the (anonymous) inner function refer to five different instances of variable j. Note that it does not work as intended if you replace let by var or if you remove the variable j and simply use the variable i in the inner function.

Scoping rules

Variables declared by let have as their scope the block in which they are defined, as well as in any sub-blocks in which they aren't redefined. In this way, let works very much like var. The main difference is that the scope of a var variable is the entire enclosing function:

  function varTest() {
    var x = 31;
    if (true) {
      var x = 71;  // same variable!
      alert(x);  // 71
    }
    alert(x);  // 71
  }

  function letTest() {
    let x = 31;
    if (true) {
      let x = 71;  // different variable
      alert(x);  // 71
    }
    alert(x);  // 31
  }

The expression to the right of the = is inside the block. This is different from how let-expressions and let-statements are scoped:

  function letTests() {
    let x = 10;

    // let-statement
    let (x = x + 20) {
      alert(x);  // 30
    }

    // let-expression
    alert(let (x = x + 20) x);  // 30

    // let-definition
    {
      let x = x + 20;  // x here evaluates to undefined
      alert(x);  // undefined + 20 ==> NaN
    }
  }

In programs and classes let does not create properties on the global object like var does; instead, it creates properties in an implicit block created for the evaluation of statements in those contexts. This essentially means that let won't override variables previously defined using var. For example:

var x = 'global';
let x = 42;
document.write(this.x + "<br>\n");

The output displayed by this code will display "global", not "42".

An implicit block is one that is not bracketed by braces; it's created implicitly by the JavaScript engine.

In functions, let executed by eval() does not create properties on the variable object (activation object or innermost binding rib) like var does; instead, it creates properties in an implicit block created for the evaluation of statements in the program. This is a consequence of eval() operating on programs in tandem with the previous rule.

In other words, when you use eval() to execute code, that code is treated as an independent program, which has its own implicit block around its code.

let-scoped variables in for loops

You can use the let keyword to bind variables locally in the scope of for loops, just like you can with var.

var i=0;
for ( let i=i ; i < 10 ; i++ )
  document.write(i + "<br>\n");

for ( let [name,value] in obj )
  document.write("Name: " + name + ", Value: " + value + "<br>\n");
Scoping rules
for (let expr1; expr2; expr3) statement

In this example, expr2, expr3, and statement are enclosed in an implicit block that contains the block local variables declared by let expr1. This is demonstrated in the first loop above.

for (let expr1 in expr2) statement
for each(let expr1 in expr2) statement

In both these cases, there are implicit blocks containing each statement. The first of these is shown in the second loop above.

Destructuring assignment

Destructuring assignment makes it possible to extract data from arrays or objects using a syntax that mirrors the construction of array and object literals.

The object and array literal expressions provide an easy way to create ad-hoc packages of data. Once you've created these packages of data, you can use them any way you want to. You can even return them from functions.

One particularly useful thing you can do with destructuring assignment is to read an entire structure in a single statement, although there are a number of interesting things you can do with them, as shown in the section full of examples that follows.

This capability is similar to features present in languages such as Perl and Python.

Examples

Destructuring assignment is best explained through the use of examples, so here are a few for you to read over and learn from.

Avoiding temporary variables

You can use destructuring assignment, for example, to swap values:

var a = 1;
var b = 3;

[a, b] = [b, a];

After executing this code, b is 1 and a is 3. Without destructuring assignment, swapping two values requires a temporary variable (or, in some low-level languages, the XOR-swap trick).

Similarly, it can be used to rotate three or more variables:

var a = 'o';
var b = "<span style='color:green;'>o</span>";
var c = 'o';
var d = 'o';
var e = 'o';
var f = "<span style='color:blue;'>o</span>";
var g = 'o';
var h = 'o';

for (lp=0;lp<40;lp++)
	{[a, b, c, d, e, f, g, h] = [b, c, d, e, f, g, h, a];
	 document.write(a+''+b+''+c+''+d+''+e+''+f+''+g+''+h+''+"<br />");}

After executing this code, a visual colorful display of the variable rotation will be displayed.

Returning to our Fibonacci generator example from above, we can eliminate the temporary "t" variable by computing the new values of "i" and "j" in a single group-assignment statement:

function fib() {
  var i = 0, j = 1;
  while (true) {
    yield i;
    [i, j] = [j, i + j];
  }
}

var g = fib();
for (let i = 0; i < 10; i++)
  print(g.next());
Multiple-value returns

Thanks to destructuring assignment, functions can return multiple values. While it's always been possible to return an array from a function, this provides an added degree of flexibility.

function f() {
  return [1, 2];
}

As you can see, returning results is done using an array-like notation, with all the values to return enclosed in brackets. You can return any number of results in this way. In this example, f() returns the values {{ mediawiki.external('1, 2') }} as its output.

var a, b;
[a, b] = f();
document.write ("A is " + a + " B is " + b + "<br>\n");

The command {{ mediawiki.external('a, b') }} = f() assigns the results of the function to the variables in brackets, in order: a is set to 1 and b is set to 2.

You can also retrieve the return values as an array:

var a = f();
document.write ("A is " + a);

In this case, a is an array containing the values 1 and 2.

Looping across objects

You can also use destructuring assignment to pull data out of an object:

let obj = { width: 3, length: 1.5, color: "orange" };

for (let [name, value] in Iterator(obj)) {
  document.write ("Name: " + name + ", Value: " + value + "<br>\n");
}

This loops over all the key/value pairs in the object obj and displays their names and values. In this case, the output looks like the following:

Name: width, Value: 3
Name: length, Value: 1.5
Name: color, Value: orange

The Iterator() around obj is not necessary in JavaScript 1.7; however, it is be needed for JavaScript 1.8. This is to allow destructuring assignment with arrays (see {{ Bug("366941") }}).

Looping across values in an array of objects

You can loop over an array of objects, pulling out fields of interest from each object:

var people = [
  {
    name: "Mike Smith",
    family: {
      mother: "Jane Smith",
      father: "Harry Smith",
      sister: "Samantha Smith"
    },
    age: 35
  },
  {
    name: "Tom Jones",
    family: {
      mother: "Norah Jones",
      father: "Richard Jones",
      brother: "Howard Jones"
    },
    age: 25
  }
];

for each (let {name: n, family: { father: f } } in people) {
  document.write ("Name: " + n + ", Father: " + f + "<br>\n");
}

This pulls the name field into n and the family.father field into f, then prints them. This is done for each object in the people array. The output looks like this:

Name: Mike Smith, Father: Harry Smith
Name: Tom Jones, Father: Richard Jones
Ignoring some returned values

You can also ignore return values that you're not interested in:

function f() {
  return [1, 2, 3];
}

var [a, , b] = f();
document.write ("A is " + a + " B is " + b + "<br>\n");

After running this code, a is 1 and b is 3. The value 2 is ignored. You can ignore any (or all) returned values this way. For example:

[,,,] = f();
Pulling values from a regular expression match

When the regular expression exec() method finds a match, it returns an array containing first the entire matched portion of the string and then the portions of the string that matched each parenthesized group in the regular expression. Destructuring assignment allows you to pull the parts out of this array easily, ignoring the full match if it is not needed.

// Simple regular expression to match http / https / ftp-style URLs.
var parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url);
if (!parsedURL)
  return null;
var [, protocol, fullhost, fullpath] = parsedURL;

{{ languages( { "es": "es/Novedades_en_JavaScript_1.7", "fr": "fr/Nouveaut\u00e9s_dans_JavaScript_1.7", "it": "it/Novit\u00e0_in_JavaScript_1.7", "ja": "ja/New_in_JavaScript_1.7", "pl": "pl/Nowo\u015bci_w_JavaScript_1.7", "zh-tw": "zh_tw/JavaScript_1.7_\u65b0\u9bae\u4e8b" } ) }}

Revision Source

<p>{{ Fx_minversion_header("2") }} JavaScript 1.7 is a language update introducing several new features, in particular generators, iterators, array comprehensions, <code>let</code> expressions, and destructuring assignment. It also includes all the features of <a href="/en/New_in_JavaScript_1.6" title="en/New_in_JavaScript_1.6">JavaScript 1.6</a>.</p>
<p>JavaScript 1.7 support was introduced in Firefox 2.</p>
<p>The code samples included in this article can be experimented with in the JavaScript shell. Read <a href="/en/Introduction_to_the_JavaScript_shell" title="en/Introduction_to_the_JavaScript_shell">Introduction to the JavaScript shell</a> to learn how to build and use the shell.</p>
<h3 name="Using_JavaScript_1.7">Using JavaScript 1.7</h3>
<p>In order to use some of the new features of JavaScript 1.7, you need to specify that you wish to use JavaScript 1.7. In HTML or XUL code, use:</p>
<pre class="brush: html"> &lt;script type="application/javascript;version=1.7"/&gt;
</pre>
<p>When using the <a href="/en/Introduction_to_the_JavaScript_shell" title="en/Introduction_to_the_JavaScript_shell">JavaScript shell</a>, you need to set the version you wish to use using the <code>-version 170</code> switch on the command line or using the <code>version()</code> function:</p>
<pre class="brush: js"> version(170);
</pre>
<p>The features that require the use of the new keywords "yield" and "let" require you to specify version 1.7 because existing code might use those keywords as variable or function names. The features that do not introduce new keywords (destructuring assignment and array comprehensions) can be used without specifying the JavaScript version.</p>
<h3 name="Generators_and_iterators">Generators and iterators</h3>
<p>When developing code that involves an iterative algorithm (such as iterating over a list, or repeatedly performing computations on the same data set), there are often state variables whose values need to be maintained for the duration of the computation process. Traditionally, you have to use a callback function to obtain the intermediate values of an iterative algorithm.</p>
<h4 name="Generators">Generators</h4>
<p>Consider this iterative algorithm that computes Fibonacci numbers:</p>
<pre class="brush: js">function do_callback(num) {
  document.write(num + "&lt;br&gt;\n");
}

function fib() {
  var i = 0, j = 1, n = 0;
  while (n &lt; 10) {
    do_callback(i);
    var t = i;
    i = j;
    j += t;
    n++;
  }
}

fib();
</pre>
<p>This code uses a callback routine to perform operations on each iterative step of the algorithm. In this case, each Fibonacci number is simply printed to the console.</p>
<p>Generators and iterators work together to provide a new, better, way to do this. Let's see how the Fibonacci number routine looks written using a generator:</p>
<p>
</p><pre class="brush: js">function fib() {
  var i = 0, j = 1;
  while (true) {
    yield i;
    var t = i;
    i = j;
    j += t;
  }
}

var g = fib();
for (var i = 0; i &lt; 10; i++) {
  document.write(g.next() + "&lt;br&gt;\n");
}
</pre>
<p>
</p><p>The function containing the <code>yield</code> keyword is a generator. When you call it, its formal parameters are bound to actual arguments, but its body isn't actually evaluated. Instead, a generator-iterator is returned. Each call to the generator-iterator's <code>next()</code> method performs another pass through the iterative algorithm. Each step's value is the value specified by the <code>yield</code> keyword. Think of <code>yield</code> as the generator-iterator version of <code>return</code>, indicating the boundary between each iteration of the algorithm. Each time you call <code>next()</code>, the generator code resumes from the statement following the <code>yield</code>.</p>
<p>You cycle a generator-iterator by repeatedly calling its <code>next()</code> method until you reach your desired result condition. In this example, we can obtain however many Fibonacci numbers we want by continuing to call <code>g.next()</code> until we have the number of results we want.</p>
<h5 name="Resuming_a_generator_at_a_specific_point">Resuming a generator at a specific point</h5>
<p>Once a generator has been started by calling its <code>next()</code> method, you can use <code>send()</code>, passing a specific value that will be treated as the result of the last <code>yield</code>. The generator will then return the operand of the subsequent <code>yield</code>.</p>
<p>You can't start a generator at an arbitrary point; you must start it with <code>next()</code> before you can <code>send()</code> it a specific value.</p>
<div class="note"><strong>Note:</strong> As a point of interest, calling <code>send(undefined)</code> is equivalent to calling <code>next()</code>. However, starting a newborn generator with any value other than undefined when calling <code>send()</code> will result in a <code>TypeError</code> exception.</div>
<h5 name="Exceptions_in_generators">Exceptions in generators</h5>
<p>You can force a generator to throw an exception by calling its <code>throw()</code> method, passing the exception value it should throw. This exception will be thrown from the current suspended context of the generator, as if the <code>yield</code> that is currently suspended were instead a <code>throw <em>value</em></code> statement.</p>
<p>If a yield is not encountered during the processing of the thrown exception, then the exception will propagate up through the call to <code>throw()</code>, and subsequent calls to <code>next()</code> will result in <code>StopIteration</code> being thrown.</p>
<h5 name="Closing_a_generator">Closing a generator</h5>
<p>Generators have a <code>close()</code> method that forces the generator to close itself. The effects of closing a generator are:</p>
<ol> <li>Any <code>finally</code> clauses active in the generator function are run.</li> <li>If a <code>finally</code> clause throws any exception other than <code>StopIteration</code>, the exception is propagated to the caller of the <code>close()</code> method.</li> <li>The generator terminates.</li>
</ol>
<h5 name="Generator_Example">Generator Example</h5>
<p>This code drives a generator that will yield every 100 loops.</p>
<pre class="brush: js">var gen = generator();

function driveGenerator() {
  if (gen.next()) {
    window.setTimeout(driveGenerator, 0);	
  } else {
    gen.close();	
  }
}

function generator() {
  while (i &lt; something) {
    /** stuff **/

    ++i;
    /** 100 loops per yield **/
    if ((i % 100) == 0) {
      yield true;
    } 
  }
  yield false;
}
</pre>
<h4 name="Iterators">Iterators</h4>
<p>An <em>iterator</em> is a special object that lets you iterate over data.</p>
<p>In normal usage, iterator objects are "invisible"; you won't need to operate on them explicitly, but will instead use JavaScript's <a href="/en/Core_JavaScript_1.5_Guide/Object_Manipulation_Statements" title="en/Core_JavaScript_1.5_Guide/Object_Manipulation_Statements"><code>for...in</code> and <code>for each...in</code> statements</a> to loop naturally over the keys and/or values of objects.</p>
<pre class="brush: js">var objectWithIterator = getObjectSomehow();

for (var i in objectWithIterator)
{
  document.write(objectWithIterator[i] + "&lt;br&gt;\n");
}
</pre>
<p>If you are implementing your own iterator object, or have another need to directly manipulate iterators, you'll need to know about the <code>next</code> method, the <code>StopIteration</code> exception, and the <code>__iterator__</code> method.</p>
<p>You can create an iterator for an object by calling <code>Iterator(<em>objectname</em>)</code>; the iterator for an object is found by calling the object's <code>__iterator__</code> method. If no <code>__iterator__</code> method is present, a default iterator is created. The default iterator yields the object's properties, according to the usual <code>for...in</code> and <code>for each...in</code> model. If you wish to provide a custom iterator, you should override the <code>__iterator__</code> method to return an instance of your custom iterator. To get an object's iterator from script, you should use <code>Iterator(<em>obj</em>)</code> rather than accessing the <code>__iterator__</code> property directly. The former works for Arrays; the latter doesn't.</p>
<p>Once you have an iterator, you can easily fetch the next item in the object by calling the iterator's <code>next()</code> method. If there is no data left, the <code>StopIteration</code> exception is thrown.</p>
<p>Here's a simple example of direct iterator manipulation:</p>
<pre class="brush: js">var obj = {name:"Jack Bauer", username:"JackB", id:12345, agency:"CTU",
          region:"Los Angeles"}; 

var it = Iterator(obj);

try {
  while (true) {
    print(it.next() + "\n");
  }
} catch (err if err instanceof StopIteration) {
  print("End of record.\n");
} catch (err) {
  print("Unknown error: " + err.description + "\n");
}
</pre>
<p>The output from this program looks like this:</p>
<pre class="eval">name,Jack Bauer
username,JackB
id,12345
agency,CTU
region,Los Angeles
End of record.
</pre>
<p>You can, optionally, specify a second parameter when creating your iterator, which is a boolean value that indicates whether or not you only want the keys returned each time you call its <code>next()</code> method. This parameter is passed in to user-defined <code>__iterator__</code> functions as its single argument. Changing <code>var it = Iterator(obj);</code> to <code>var it = Iterator(obj, true);</code> in the above sample results in the following output:</p>
<pre>name
username
id
agency
region
End of record.
</pre>
<p>In both cases, the actual order in which the data is returned may vary based on the implementation. There is no guaranteed ordering of the data.</p>
<p>Iterators are a handy way to scan through the data in objects, including objects whose content may include data you're unaware of. This can be particularly useful if you need to preserve data your application isn't expecting.</p>
<h3 name="Array_comprehensions">Array comprehensions</h3>
<p>Array comprehensions are a use of generators that provides a convenient way to perform powerful initialization of arrays. For example:</p>
<pre class="brush: js">function range(begin, end) {
  for (let i = begin; i &lt; end; ++i) {
    yield i;
  }
}
</pre>
<p><code>range()</code> is a generator that returns all the values between <var>begin</var> and <var>end</var>. Having defined that, we can use it like this:</p>
<pre class="brush: js">var ten_squares = [i * i for each (i in range(0, 10))];
</pre>
<p>This pre-initializes a new array, <var>ten_squares</var>, to contain the squares of the values in the range <code>0..9</code>.</p>
<p>You can use any conditional when initializing the array. If you want to initialize an array to contain the even numbers between 0 and 20, you can use this code:</p>
<pre class="brush: js">var evens = [i for each (i in range(0, 21)) if (i % 2 == 0)];
</pre>
<p>Prior to JavaScript 1.7, this would have to be coded something like this:</p>
<pre class="brush: js">var evens = [];
for (var i=0; i &lt;= 20; i++) {
  if (i % 2 == 0)
    evens.push(i);
}
</pre>
<p>Not only is the array comprehension much more compact, but it's actually easier to read, once you're familiar with the concept.</p>
<h4 name="Scoping_rules">Scoping rules</h4>
<p>Array comprehensions have an implicit block around them, containing everything inside the square brackets, as well as implicit <code>let</code> declarations.</p>
<h3 name="Block_scope_with_let">Block scope with <code>let</code></h3>
<p>There are several ways in which <code>let</code> can be used to manage block scope of data and functions:</p>
<ul> <li>The <strong><code>let</code> statement</strong> provides a way to associate values with variables within the scope of a block, without affecting the values of like-named variables outside the block.</li> <li>The <strong><code>let</code> expression</strong> lets you establish variables scoped only to a single expression.</li> <li>The <strong><code>let</code> definition</strong> defines variables whose scope is constrained to the block in which they're defined. This syntax is very much like the syntax used for <code>var</code>.</li> <li>You can also use <code>let</code> to establish variables that exist only within the context of a <code>for</code> loop.</li>
</ul>
<h4 name="let_statement"><code>let</code> statement</h4>
<p>The <code>let</code> statement provides local scoping for variables. It works by binding zero or more variables in the lexical scope of a single block of code; otherwise, it is exactly the same as a <a href="/en/Core_JavaScript_1.5_Reference/Statements/block" title="en/Core_JavaScript_1.5_Reference/Statements/block">block statement</a>. Note in particular that the scope of a variable declared inside a <code>let</code> statement using <code>var</code> is still the same as if it had been declared outside the <code>let</code> statement; such variables still have function scoping.</p>
<p>For example:</p>
<pre class="brush: js">var x = 5;
var y = 0;

let (x = x+10, y = 12) {
  print(x+y + "\n");
}

print((x + y) + "\n");
</pre>
<p>The output from this program will be:</p>
<pre class="eval">27
5
</pre>
<p>The rules for the code block are the same as for any other code block in JavaScript. It may have its own local variables established using the <code>let</code> declarations.</p>
<div class="note"><strong>Note:</strong> When using the <code>let</code> statement syntax, the parentheses following <code>let</code> are required. Failure to include them will result in a syntax error.</div>
<h5 name="Scoping_rules_2">Scoping rules</h5>
<p>The scope of variables defined using <code>let</code> is the <code>let</code> block itself, as well as any inner blocks contained inside it, unless those blocks define variables by the same names.</p><h4 name="let_expressions"><code>let</code> expressions</h4>
<p>You can use <code>let</code> to establish variables that are scoped only to a single expression:</p>
<p>
</p><pre class="brush: js">var x = 5;
var y = 0;
document.write( let(x = x + 10, y = 12) x+y  + "&lt;br&gt;\n");
document.write(x+y + "&lt;br&gt;\n");
</pre>
<p>
</p><p>The resulting output is:</p>
<pre>27
5
</pre>
<p>In this case, the binding of the values of <var>x</var> and <var>y</var> to <code>x+10</code> and <code>12</code> are scoped solely to the expression <code>x+y</code>.</p>
<h5 name="Scoping_rules_3">Scoping rules</h5>
<p>Given a <code>let</code> expression:</p>
<pre class="eval">let (<var>decls</var>) <var style="color: blue;">expr</var>
</pre>
<p>There is an implicit block created around <var style="color: blue;">expr</var>.</p>
<h4 name="let_definitions"><code>let</code> definitions</h4>
<p>The <code>let</code> keyword can also be used to define variables inside a block.</p>
<div class="note"><strong>Note:</strong> If you have more interesting examples of ways to use <code>let</code> definitions, please consider adding them here.</div>
<pre class="brush: js">if (x &gt; y)
{
   let gamma = 12.7 + y;
   i = gamma * x;
}
</pre>
<p><code>let</code> statements, expressions and definitions sometimes make the code cleaner when inner functions are used.</p>
<pre class="brush: js">var list = document.getElementById("list");
for (var i = 1; i &lt;= 5; i++)
{
  var item = document.createElement("LI");
  item.appendChild(document.createTextNode("Item " + i));
  let j = i;
  item.onclick = function (ev) {
    alert("Item " + j + " is clicked.");
  };
  list.appendChild(item);
}
</pre>
<p>The example above works as intended because the five instances of the (anonymous) inner function refer to five different instances of variable <code>j</code>. Note that it does not work as intended if you replace <code>let</code> by <code>var</code> or if you remove the variable <code>j</code> and simply use the variable <code>i</code> in the inner function.</p>
<h5 name="Scoping_rules_4">Scoping rules</h5>
<p>Variables declared by <code>let</code> have as their scope the block in which they are defined, as well as in any sub-blocks in which they aren't redefined. In this way, <code>let</code> works very much like <code>var</code>. The main difference is that the scope of a <code>var</code> variable is the entire enclosing function:</p>
<pre class="brush: js">  function varTest() {
    var x = 31;
    if (true) {
      var x = 71;  // same variable!
      alert(x);  // 71
    }
    alert(x);  // 71
  }

  function letTest() {
    let x = 31;
    if (true) {
      let x = 71;  // different variable
      alert(x);  // 71
    }
    alert(x);  // 31
  }
</pre>
<p>The expression to the right of the <code>=</code> is inside the block. This is different from how <code>let</code>-expressions and <code>let</code>-statements are scoped:</p>
<pre class="brush: js">  function letTests() {
    let x = 10;

    // let-statement
    let (x = x + 20) {
      alert(x);  // 30
    }

    // let-expression
    alert(let (x = x + 20) x);  // 30

    // let-definition
    {
      let x = x + 20;  // x here evaluates to undefined
      alert(x);  // undefined + 20 ==&gt; NaN
    }
  }
</pre>
<p>In programs and classes <code>let</code> does not create properties on the global object like <code>var</code> does; instead, it creates properties in an implicit block created for the evaluation of statements in those contexts. This essentially means that <code>let</code> won't override variables previously defined using <code>var</code>. For example:</p>
<pre class="brush: js">var x = 'global';
let x = 42;
document.write(this.x + "&lt;br&gt;\n");
</pre>
<p>The output displayed by this code will display "global", not "42".</p>
<p>An <em>implicit block</em> is one that is not bracketed by braces; it's created implicitly by the JavaScript engine.</p>
<p>In functions, <code>let</code> executed by <code>eval()</code> does not create properties on the variable object (activation object or innermost binding rib) like <code>var</code> does; instead, it creates properties in an implicit block created for the evaluation of statements in the program. This is a consequence of <code>eval()</code> operating on programs in tandem with the previous rule.</p>
<p>In other words, when you use <code>eval()</code> to execute code, that code is treated as an independent program, which has its own implicit block around its code.</p>
<h4 name="let-scoped_variables_in_for_loops"><code>let</code>-scoped variables in <code>for</code> loops</h4>
<p>You can use the <code>let</code> keyword to bind variables locally in the scope of <code>for</code> loops, just like you can with <code>var</code>.</p>
<pre class="brush: js">var i=0;
for ( let i=i ; i &lt; 10 ; i++ )
  document.write(i + "&lt;br&gt;\n");

for ( let [name,value] in obj )
  document.write("Name: " + name + ", Value: " + value + "&lt;br&gt;\n");
</pre>
<h5 name="Scoping_rules_5">Scoping rules</h5>
<pre class="eval">for (let <var>expr1</var>; <var style="color: blue;">expr2</var>; <var style="color: blue;">expr3</var>) <var style="color: blue;">statement</var>
</pre>
<p>In this example, <var style="color: blue;">expr2</var>, <var style="color: blue;">expr3</var>, and <var style="color: blue;">statement</var> are enclosed in an implicit block that contains the block local variables declared by <code>let <var>expr1</var></code>. This is demonstrated in the first loop above.</p>
<pre class="eval">for (let <var>expr1</var> in <var>expr2</var>) <var style="color: blue;">statement</var>
for each(let <var>expr1</var> in <var>expr2</var>) <var style="color: blue;">statement</var>
</pre>
<p>In both these cases, there are implicit blocks containing each <var style="color: blue;">statement</var>. The first of these is shown in the second loop above.</p>
<h3 name="Destructuring_assignment">Destructuring assignment</h3>
<p>Destructuring assignment makes it possible to extract data from arrays or objects using a syntax that mirrors the construction of array and object literals.</p>
<p>The object and array literal expressions provide an easy way to create ad-hoc packages of data. Once you've created these packages of data, you can use them any way you want to. You can even return them from functions.</p>
<p>One particularly useful thing you can do with destructuring assignment is to read an entire structure in a single statement, although there are a number of interesting things you can do with them, as shown in the section full of examples that follows.</p>
<p>This capability is similar to features present in languages such as Perl and Python.</p>
<h4 name="Examples">Examples</h4>
<p>Destructuring assignment is best explained through the use of examples, so here are a few for you to read over and learn from.</p>
<h5 name="Avoiding_temporary_variables">Avoiding temporary variables</h5>
<p>You can use destructuring assignment, for example, to swap values:</p>
<pre class="brush: js">var a = 1;
var b = 3;

[a, b] = [b, a];
</pre>
<p>After executing this code, <var>b</var> is 1 and <var>a</var> is 3. Without destructuring assignment, swapping two values requires a temporary variable (or, in some low-level languages, the <a class="external" href="http://en.wikipedia.org/wiki/XOR_swap">XOR-swap trick</a>).</p>
<p>Similarly, it can be used to rotate three or more variables:</p>
<pre class="brush: js">var a = 'o';
var b = "&lt;span style='color:green;'&gt;o&lt;/span&gt;";
var c = 'o';
var d = 'o';
var e = 'o';
var f = "&lt;span style='color:blue;'&gt;o&lt;/span&gt;";
var g = 'o';
var h = 'o';

for (lp=0;lp&lt;40;lp++)
	{[a, b, c, d, e, f, g, h] = [b, c, d, e, f, g, h, a];
	 document.write(a+''+b+''+c+''+d+''+e+''+f+''+g+''+h+''+"&lt;br /&gt;");}
</pre>
<p>After executing this code, a visual colorful display of the variable rotation will be displayed.</p>
<p>Returning to our Fibonacci generator example from above, we can eliminate the temporary "t" variable by computing the new values of "i" and "j" in a single group-assignment statement:</p>
<pre class="brush: js">function fib() {
  var i = 0, j = 1;
  while (true) {
    yield i;
    [i, j] = [j, i + j];
  }
}

var g = fib();
for (let i = 0; i &lt; 10; i++)
  print(g.next());
</pre>
<h5 name="Multiple-value_returns">Multiple-value returns</h5>
<p>Thanks to destructuring assignment, functions can return multiple values. While it's always been possible to return an array from a function, this provides an added degree of flexibility.</p>
<pre class="brush: js">function f() {
  return [1, 2];
}
</pre>
<p>As you can see, returning results is done using an array-like notation, with all the values to return enclosed in brackets. You can return any number of results in this way. In this example, <code>f()</code> returns the values <code>{{ mediawiki.external('1, 2') }}</code> as its output.</p>
<pre class="brush: js">var a, b;
[a, b] = f();
document.write ("A is " + a + " B is " + b + "&lt;br&gt;\n");
</pre>
<p>The command <code>{{ mediawiki.external('a, b') }} = f()</code> assigns the results of the function to the variables in brackets, in order: <var>a</var> is set to 1 and <var>b</var> is set to 2.</p>
<p>You can also retrieve the return values as an array:</p>
<pre class="brush: js">var a = f();
document.write ("A is " + a);
</pre>
<p>In this case, <var>a</var> is an array containing the values 1 and 2.</p>
<h5 name="Looping_across_objects">Looping across objects</h5>
<p>You can also use destructuring assignment to pull data out of an object:</p>
<pre class="brush: js">let obj = { width: 3, length: 1.5, color: "orange" };

for (let [name, value] in Iterator(obj)) {
  document.write ("Name: " + name + ", Value: " + value + "&lt;br&gt;\n");
}
</pre>
<p>This loops over all the key/value pairs in the object <var>obj</var> and displays their names and values. In this case, the output looks like the following:</p>
<pre class="eval">Name: width, Value: 3
Name: length, Value: 1.5
Name: color, Value: orange
</pre>
<p>The <code>Iterator()</code> around <code>obj</code> is not necessary in JavaScript 1.7; however, it is be needed for <a href="/en/New_in_JavaScript_1.8" title="en/New_in_JavaScript_1.8">JavaScript 1.8</a>. This is to allow destructuring assignment with arrays (see {{ Bug("366941") }}).</p>
<h5 name="Looping_across_values_in_an_array_of_objects">Looping across values in an array of objects</h5>
<p>You can loop over an array of objects, pulling out fields of interest from each object:</p>
<pre class="brush: js">var people = [
  {
    name: "Mike Smith",
    family: {
      mother: "Jane Smith",
      father: "Harry Smith",
      sister: "Samantha Smith"
    },
    age: 35
  },
  {
    name: "Tom Jones",
    family: {
      mother: "Norah Jones",
      father: "Richard Jones",
      brother: "Howard Jones"
    },
    age: 25
  }
];

for each (let {name: n, family: { father: f } } in people) {
  document.write ("Name: " + n + ", Father: " + f + "&lt;br&gt;\n");
}
</pre>
<p>This pulls the <var>name</var> field into <var>n</var> and the <var>family.father</var> field into <var>f</var>, then prints them. This is done for each object in the <var>people</var> array. The output looks like this:</p>
<pre>Name: Mike Smith, Father: Harry Smith
Name: Tom Jones, Father: Richard Jones
</pre>
<h5 name="Ignoring_some_returned_values">Ignoring some returned values</h5>
<p>You can also ignore return values that you're not interested in:</p>
<pre class="brush: js">function f() {
  return [1, 2, 3];
}

var [a, , b] = f();
document.write ("A is " + a + " B is " + b + "&lt;br&gt;\n");
</pre>
<p>After running this code, a is 1 and b is 3. The value 2 is ignored. You can ignore any (or all) returned values this way. For example:</p>
<pre class="brush: js">[,,,] = f();
</pre>
<h5 name="Pulling_values_from_a_regular_expression_match">Pulling values from a regular expression match</h5>
<p>When the regular expression <code><a href="/en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp/exec" title="en/Core_JavaScript_1.5_Reference/Global_Objects/RegExp/exec"> exec()</a></code> method finds a match, it returns an array containing first the entire matched portion of the string and then the portions of the string that matched each parenthesized group in the regular expression. Destructuring assignment allows you to pull the parts out of this array easily, ignoring the full match if it is not needed.</p>
<pre class="brush: js">// Simple regular expression to match http / https / ftp-style URLs.
var parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url);
if (!parsedURL)
  return null;
var [, protocol, fullhost, fullpath] = parsedURL;
</pre> <p>{{ languages( { "es": "es/Novedades_en_JavaScript_1.7", "fr": "fr/Nouveaut\u00e9s_dans_JavaScript_1.7", "it": "it/Novit\u00e0_in_JavaScript_1.7", "ja": "ja/New_in_JavaScript_1.7", "pl": "pl/Nowo\u015bci_w_JavaScript_1.7", "zh-tw": "zh_tw/JavaScript_1.7_\u65b0\u9bae\u4e8b" } ) }}</p>
Revert to this revision