Blog-Archiv

Samstag, 30. Dezember 2017

ES6 Modules Test Environment

ES6 modules are the crown of the ECMAScript 2015 specification. Together with block-scope (const, let) they provide the only real encapsulation-mechanism of that language version. Modules let us control the dependencies of our scripts (import), and what is visible of them (export).

Modules are →

  • file-based;
    a module imports another one by stating a path relative to its own file-system location (slash-separated);
    one file .js is one script is one module

  • singletons by nature, you can not instantiate them;
    the browser loads a module just once per page (tab, window);
    thinking in Java, all fields and functions inside a module would be static

  • → an encapsulation-mechanism;
    other modules can not see fields and functions that have not been exported

  • read-only views of exported fields (variables, constants);
    you can see an exported field of module A in module B when you imported it, but you can not change its value

Don't confuse modules with classes. Modules provide control for deployment and dependency-loading, classes provide state when instantiated to objects. Modules are meant to contain classes, or be factories for objects of classes.

ES6 modules are already available in HTML5 browsers. This article describes how you can create a test-environment (without node-js!) for studying them.

Make Modules Work in Browsers

For a deeper insight (CORS, loading order of script elements) read the article "ECMAScript modules in browsers".

  1. When Firefox, or lower than Safari 10.1, Chrome 61 or Edge 16, configure browser to support modules
    • Firefox: enter address about:config, switch dom.moduleScripts.enabled to true; Firefox also runs fine with file:/ protocol (loading page from local disk)
    • Chrome refuses to load modules when using the file:/ protocol ("blocked by CORS", "strict MIME type checking"), so you need to deploy your page-resources to some HTTP server)

  2. Set attribute type = "module" in all HTML script elements that import modules

This works for both the newest desktop- and mobile-browsers.

Example Sources

Here are the files and directories used in the following. The file es6-modules.html is the test page. It imports es6-modules.js in a <script "type = module"> element. The file es6-modules.js is a module, and it imports all other *.js files listed here, that means it depends on them.

es6-modules.html
es6-modules.js
modules
counter.js
cycle-1.js
cycle-2.js
io
output.js
math
mathematics.js

You can try out an example similar to this on my homepage (unfortunately I can not deploy modules on this Blog server:-).

HTML

es6-modules.html

Following is HTML using modules. It defines two buttons demonstrating different ways to integrate module functions:

  1. "Page-driven module call"
    When you want to call a module-function directly from a button callback, you will be confronted with the separation between module scripts and traditional scripts. It is not possible to import modules into a normal script. Thus you need to write a <script "type = module"> element that imports the required modules and sets the needed functions onto some global page-object that is reachable from callbacks, as done in const page = {} and page.foobar = foobar. Mind that you can not create the const page = {} in a <script "type = module"> element, because then it would be a local constant!

  2. "Module-driven module call"
    The other way is to write an adapter <script "type = module"> that imports the required modules and installs all callbacks itself, as done in document.getElementById("foobarTestButton").addEventListener("click", foobar). This is most likely the better way to go.
    <div>
      <button onclick="page.foobar()">Page-driven module call</button>
      <button id="foobarTestButton">Module-driven module call</button>
    </div>
    
    <fieldset>
      <legend>Success Messages</legend>
      <p id="successMessages"></p>
    </fieldset>
    
    <fieldset>
      <legend>Error Messages</legend>
      <p id="errorMessages"></p>
    </fieldset>
    
    
    <script>
        const page = {}
    </script>
    
    <script type="module">
        import foobar from "./es6-modules.js"
        
        page.foobar = foobar
    </script>
    
    <script type="module">
        import foobar from "./es6-modules.js"
        
        const foobarTestButton = document.getElementById("foobarTestButton")
        foobarTestButton.addEventListener("click", foobar)
    </script>
    
    <script type="module">
        import { configure } from "./modules/io/output.js"
        
        configure("successMessages", "errorMessages")
    </script>

Two buttons are created here. The first one has a programmed callback that calls page.foobar(). I named this "Page-driven".
The second button has an id, but no callback. The callback is installed in a script element below. I named this "Module-driven".

Next there are two output-areas "Success Messages" and "Error Messages". They will get configured to be the targets for output.js module.

The subsequent script elements do what is required to use module functions in a page-driven and a module-driven way. The first one is a non-module script, it establishes the global page object. The next one imports a module and sets the foobar module-function into the page object, to be available for the first button. The following script installs a module-function as callback into the second button. The last script element finally configures the output.js module to write to the output-areas with ids "successMessages" and "errorMessages".

Mind that currently import-paths must be written as from "./es6-modules.js", giving a bare from "es6-modules.js" would fail.
It is not a problem to import a module several times in different script-elements, the browser would load each module just once.

Library Modules

counter.js

This is a stateful module that holds a counter, and a function that can increment it. The counter field state itself can not be changed from outside (although exported), but is visible (because exported). To change it, modules must call the increment() function. There is no way to reset the counter except a browser page reload.

export let counter = 0

export function increment() {
    counter++;
}

cycle*.js

These are two modules demonstrating the ES6-ability to resolve cyclic dependencies. Module cycle-1.js defines foo() calling bar() in cycle-2.js, that again calls foo(). If there weren't preventions against recursion by holding a private state, these two functions would actually call each other in an endless loop!

cycle-1.js
import { bar } from "./cycle-2.js"

let state = "idle"

export function foo() {
    if (state === "running")
        return  /* avoid recursion */
        
    state = "running"
    try {
        bar()
    }
    finally {
        state = "idle"
    }
}
cycle-2.js
import { foo } from "./cycle-1.js"

let running = false

export function bar() {
    if (running)
        return  /* avoid recursion */
        
    running = true
    try {
        foo()
    }
    finally {
        running = false
    }
}

output.js

A stateful module that contains functions for appending success- and error-messages to specified elements. That means the module must be parameterized with the according element-ids, which can be done by calling configure(newSuccessElementId, newErrorElementId). See the last script element in es6-modules.html which does this.

let successElementId = "success"
let errorElementId = "error"

function message(text, elementId) {
    const outputElement = document.getElementById(elementId)
    outputElement.innerHTML += ( text+"<br>" )
}

export function configure(newSuccessElementId, newErrorElementId) {
    if (newSuccessElementId)
        successElementId = newSuccessElementId
        
    if (newErrorElementId)
        errorElementId = newErrorElementId
}

export function success(text) {
    message(text, successElementId)
}

export function error(text) {
    message(text, errorElementId)
}

mathematics.js

This is an example for a stateless module that contains lots of functions around a specific topic like mathematics.

export function add(first, second) {
    return first + second
}

export function multiply(first, second) {
    return first * second
}

Page Module

es6-modules.js

Imports and uses all other modules. Its foobar() function performs various tests with them. You can launch it by clicking on one of the buttons in the HTML page. Outputs will go to the configured areas.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import { success, error } from "./modules/io/output.js"
import { counter, increment } from "./modules/counter.js"
import * as mathematics from "./modules/math/mathematics.js"
import { foo as cycleTest } from "./modules/cycle-1.js"

export default function foobar() {

    /* use module "counter" */
    
    try {
        counter = 99  /* module fields are readonly! */
    }
    catch (errorMessage) {
        error(errorMessage)
    }
    
    try {
        const oldValue = counter
        increment()
        if (oldValue + 1 !== counter)
            error("counter didn't increment: "+counter)
        else
            success("counter after increment: "+counter)
    }
    catch (errorMessage) {
        error(errorMessage)
    }
    
    /* use module "mathematics" */
    
    const oneAndTwo = mathematics.add(1, 2)
    if (oneAndTwo !== 3)
        error("1 + 2 is not 3: "+oneAndTwo)
    else
        success("add() worked: 1 + 2 == 3")
        
    const twoThreeTimes = mathematics.multiply(2, 3)
    if (twoThreeTimes !== 6)
        error("2 * 3 is not 6: "+twoThreeTimes)
    else
        success("multiply() worked: 2 * 3 == 6")
    
    /* use cyclic modules */
    
    try {
        cycleTest()
        success("foo() is available despite cyclic dependency")
    }
    catch (errorMessage) {
        error(errorMessage)
    }
}

The import { success, error } from "./modules/io/output.js" imports just the needed functions. The configure() call already has been done in es6-modules.html, and only there the ids of the message-elements should be duplicated.

We import { counter, increment } from "./modules/counter.js" to also see the field counter. Subsequently writing to that field gets tested, this results in an exception (→ fields are exported as read-only views!).

The module mathematics.js is an example for a collection of many functions around mathematics, and let's assume that we also need to use most of them. Thus mathematics.js is imported using the asterisk-wildcard, to be seen in in import * as mathematics from "modules/math/mathematics.js". Mind that the "as alias" is required when importing that way!

Finally one of the cyclic modules gets imported. I rename the foo function to cycleTest using an alias. When no exception gets thrown on calling cycleTest(), the dependency-cycle was resolved correctly by ES6.

The export default function foobar() statement exports the function foobar() in a way that it can be imported without { destructuring }, to be seen in es6-modules.html. Mind that just one default export is possible per module! Further it is not possible to use export default together with var, let or const.

The code of foobar() performs various tests on the imported module fields and functions. It should be easy to see what it does, so I won't explain it in further detail.

Resume

We should keep in mind that modules can hold state, but that state would be the same for all components inside the HTML page. As soon as you implement a TABLE module, and you have more than one TABLE instances in your page that use this module, they would compete for the state (singleton!). Remember that static methods in Java never should refer to any field other than its parameters. ES6 modules have to be regarded as "static" implementations (in the sense of Java), and you should use them as factories for class-instances that represent state-holding objects, and only in that class all functionality should be implemented. (Thinking further, that everything in classes is public, i.e. no encapsulation at all, what remains from the beautiful encapsulation-mechanism of modules, non-exported fields and functions not being visible? Nothing.)

It took me much more time to make this example work in Firefox and Chrome than writing it. I had to experiment a lot with import statements and file extensions. Finding out that absolute paths are supported by ES6, but are absolute to the file-system root (what else?), was a little frustrating. Why would we need such? It binds ES6 projects to the file system of a certain machine! We are hardcoding the directory-structure of our library also with relative path imports, and this is hard enough to maintain (imagine that directory structure once changes!). The third import alternative (to relative and absolute paths) is "module specifiers". But not even the specification states how this should be organized.

Topic of my next Blog will be: which of the (too) many provided export/import possibilities actually make sense?




Mittwoch, 27. Dezember 2017

ES6 Template Literals

ES6: ?

The vision of building HTML components using just ES6 becomes realistic with Template Literals. They provide a templating mechanism that is built into browsers.

An ES6 template literal is text enclosed in `backticks`, possibly spanning several lines, containing placeholders given by the popular ${expression} syntax (→ interpolation expressions). Template literals can contain other template literals.
ES6 defines template literals and tagged template literals.

Here is an example template literal, alerting "Hello Dolly":

const world = "Dolly"
const output = `Hello ${world}`
alert(output)

And here is an example for a tagged template literal (the tag-function not yet implemented, thus it would show nothing):

function formatHelloWorld(textPartsArray, ...parameters) {
    let resultText = ""
    // process input to resultText
    return resultText
}
const world = "Dolly"
const output = formatHelloWorld `Hello ${world}`
alert(output)

The formatHelloWorld tag-function actually gets called when executing this source, although there are no invocation-parentheses. It receives a parsed representation of the template literal behind it, including placeholder values, and it must output text. There can be optional spaces between the function-name and the first backtick, even newlines, but mostly the literal is directly appended to the name: formatHelloWorld`Hello ${world}`

Here are some explanations about template literals from the web:

  • Template literals are string literals with support for interpolation and multiple lines.
  • A template literal is code that is executed immediately. It is basically a function: data in, text out.
  • Tagged template literals are function calls whose parameters are provided via template literals.

Study the "Template Function" example to find out how powerful this concept is when combined with arrow-functions!

ES6 Template Literal Tests

Click onto one of the buttons to get an example script into the text area. Below the script you find an explanation.

  •  
  •  
  •  
  •  
  •  
Description

Resume

Reminiscences to the UNIX shell `command substitution` come up. But template literals are different, especially the tagged ones. Nevertheless both do the same: embedding the result of an expression into text. The aim of template literals is most likely to embed data-contents into HTML-layout, preparing for building web components via ES6.




Template literals start and end with ` (backtick). If there is a backtick inside, it must be escaped by \ (backslash). Template literals can span multiple lines, all kinds of whitespace inside will be preserved. Interpolated expressions, like ${expression}, will be substituted as soon as the code is executed where the template literal resides.

This example shows several techniques. Data can be referenced via complex expressions like ${persons[0].name}. The special character '$' doesn't need to be escaped as long as it is not '${'. Due to preservation of spaces you can not indent source text inside backticks (unless spaces don't matter in the result text).

Tagged template literals are preceded by a function name. The backtick-literal will be passed to the function in a parsed state, having evaluated all contained interpolation expressions.

This example shows how a tag function can be implemented. It receives, as first parameter, an array of those text parts that are outside interpolation expressions, and, as rest-parameter, all interpolation expressions, already replaced by actual values. The tagFunction example implementation wraps all rest-parameters into HTML <b> tags.

Mind that the ${money} text-part must be escaped by backslash to avoid its interpretation as interpolation expression.

This example shows how powerful template literals could be used for building HTML components. An array of objects gets converted into a HTML table containing the property values of the objects. To achieve this, arrow-functions are declared that evaluate to template literals that can contain other template literal functions. Calling the template literal function finally with actual data gives a fully substituted HTML-text that can be used to as innerHTML of an element.

Special characters inside substituted data will be interpreted as HTML control-characters by the browser, e.g. <, > or &, and thus must be escaped.

This example uses the tag-function html() to escape substituted interpolation expressions, e.g. contained in the test-string "'James' <Bond> & Co.".
Further it facilitates the usage of unescaped backslashes inside the template literal, to be seen as <i>\Company\</i>. The tag-function does this by using textParts.raw, which delivers uninterpreted text fragments. The textParts parameter is a special array that provides that raw property.

Why does the expected text contain double backslashes around "Company"? Because the default tag function, applied when no explicit tag-function was given, does not use textParts.raw, it interprets all backslashes, so we must escape them.

This example shows that you can do a lot inside an interpolated expression. Here two functions are called, and their return is built together to a string representing first and last name of a person.

Dienstag, 26. Dezember 2017

ES6 Miscellaneous New Features

ES6: ?

In recent Blogs I resumed the most important new ES6 features. What I left out was

In this article I will make up some other smaller ES6 topics I didn't mention yet.

ES6 Miscellaneous Tests

Click onto one of the buttons to get an example script into the text area. Below the script you find an explanation.

  •  
  •  
  •  
  •  
  •  
  •  
Description

Resume

All new ES6 features shown in this article, except proxying, have been missing in JavaScript. They are useful and valuable. Future will show whether the proxying features will make our source maintenance easier or harder. Code that is hard to read will always require more time.

As we talk about new features, there are also new Number functionalities you should look at. Did you know that is false in JavaScript, and ES6 doesn't change that?




Default parameters are assigned by simply adding an = value to the parameter declaration in function head. In this example, the function assignDefaultsToParameters() assigns "John" as default for parameter firstName, and "Doe" for lastName.

The assertions in function assignDefaultsToParameters() use the global JS variable arguments to check how many parameters have been passed to the function. That way it can ensure that mixing-in defaults has been executed correctly.

The get and set functions actually define a property that you can read from and write to. If no set is present, a write to that property will be simply ignored (no exception is thrown!).

The ES6 syntax is
get propertyName() { /* body */ }
and
set propertyName(singleValue) { /* body */ }
The body can contain any implementation you need.

This example defines an test-object with a property myName, and a getter/setter tuple that declares a property name (which actually doesn't exist). The getter returns the current value of myName property, with a prefix. The setter denies any new name by throwing an exception.

Mind that the underlying property myName still is publicly visible and writable, so this example does not demonstrate a safe write-protection mechanism!

get and set have become kind of keywords in ES6. You couldn't call testObject.name (without parentheses!) when the ES6-interpreter wouldn't treat them specially.

The new ES6 Proxy class facilitates to intercept certain operations on an object, like reading a property's value, writing to a property, or finding out whether a property exists (and some more).

This example shows a proxyDelegate wrapping a testObject. The proxy intercepts read- and write-access to any property, and the in operator.
When reading a property, it would throw an exception in case the property does not exist in the wrapped origin object.
When writing a property, it would do the same, but with a different message.
When testing whether a property is contained in wrapped object, it returns false for "name", but forwards to the in-operator of origin in any other case.

A Set is a collection that contains no duplicates.
A Map is a key-value container that contains no duplicate keys.

This example sets up a Set and tries out if duplicates are recognized. Then it sets up a Map and does the same. (Mind that a map.set() call will overwrite any preceding of the same key.) In a for-of loop it checks that no key has been stringified. Finally it ensures that also symbols and functions can be keys in maps. Try that out for Set!

The difference between a Map and an Object is that Object can not contain a key that is not a string (exceptions are ES6 symbol properties). That means, if some added key is not a string, it is stringified by the JS interpreter itself. Only the new Map provides us left-side keys that can be of any data type.

Consider also using WeakMap and WeakSet. I didn't give them an example here because garbage collection can not be triggered explicitly, and they are neither Iterable nor do they have a size property, thus they are not testable.

The new built-in class Intl.DateTimeFormat allows locale-specific date formattings. Intl most likely stands for "internationalization". (What a horrible long word!-)

You need to pass a string describing the locale to the constructor. (Do we have symbols for these?)
The example demonstrates this for American, English and German locales. Mind that they are all different!

There is also a new built-in class Intl.NumberFormat for locale-specific number formattings. Mind how fraction digits can be restricted via options-parameter. There are lots more options.

Sonntag, 17. Dezember 2017

ES6 Arrow Functions

ES6: ?

ES6 uses the term "arrow function" for what is called "lambda" in other programming languages. The idea is having an anonymous function that has no name, but could receive arguments, is able to return a value, and can use fields and functions of the surrounding environment (lexical scope). Related terms are "closure" and "continuation", they also use the lexical scope.

Here is an example of an ES6 lambda:

const numbers = [ 1, 2, 3, 4 ]
const evenNumbers = []
const oddNumbers = []

numbers.forEach((number) => {
  if (number % 2 === 0)
    evenNumbers.push(number)
  else
    oddNumbers.push(number)
})

This separates even from odd numbers. The Array.forEach() function expects a function as parameter, which is implemented as an arrow-function here. It has no name, the declared parameter (number) denotes the received array element. The => arrow points to the body, which can have different shapes. This one tests the parameter, and adds it to either the even or odd array. The forEach() function calls the nested arrow-function with each array-element.

Here is the same functionality implemented in JavaScript:

....

numbers.forEach(function(number) {
  if (number % 2 === 0)
    evenNumbers.push(number);
  else
    oddNumbers.push(number);
});

Not very much difference actually. The only difference is that an arrow-function refers to the "this" pointer of the environment (lexical scope) when it is inside a class or object. That makes it more intuitive compared to an anonymous JS function nested into another function, which does not know "this" any more. Read example "Handling of this" for more insight.

ES6 Arrow Function Tests

Click onto one of the buttons to get an example script into the text area. Below the script you find an explanation.

  •  
  •  
  •  
Description

Resume

Arrow functions (lambdas) are not so very different from anonymous JS functions, just accessing the "this" pointer has become more intuitive. Lambdas will be quite useful for small conversion tasks. But mind that they are anonymous, and thus not made to be reused, so they may promote code duplications!




This example shows the smallest possible arrow function. When there are no parameters to the function, the empty parentheses are required. The body of the function is "beep", nothing else. When there is just one statement in an arrow-function body, or the statements are wrapped into parentheses and separated by commas, the last statement's result is taken as return-value (see "Shapes of Lambdas" example). So the only thing this lambda does is returning "beep".

Mind that here the arrow-function is not anonymous, it was given a name explicitly, this is not a typical usage.

In second block you see the same functionality implemented in JavaScript.
It is useful in both languages to use semicolons, although I'm leaving it out in ES6 examples because this was recommended.

Array.map() generates a copy of its array. The function given as parameter to map() is executed for each member of the original array, and its return object is put into the new array. That's what lambdas are for: short conversions that don't need to be reusable or overridable.

The lambdas of this example increment a given number by 1, and thus we can generate an array of odd numbers from an array of even numbers. The example shows different ways to write such a function, all lambdas here do nearly the same.

We can leave out the parentheses around the parameter when there is just one parameter. The same applies to the function body, curly braces (or parentheses) are needed just when the body consists of more than one statement. Good style is to use curly braces and stating the return explicitly. When using parentheses and commas to wrap several statements inside, you must leave out the return in last statement.

There are two classes that both collect indexes of even and odd numbers into arrays called this.evenIndexes and this.oddIndexes. The first works with a lambda in forEach through the given array, the second uses a conventional function to do that.

Try to get rid of const that = this in WithFunction class. It is not possible, you would get "TypeError: this is undefined". You can access the "this" pointer from inside the function only by saving it to an outer variable, here called "that".

The same applies when using "this" in a function nested into an object method.

For conventional functions, the value of "this" is determined by how they are called. Arrow-functions have a lexical "this", the value being determined by the surrounding scope. This applies to keywords this and super, and also to arguments.

Sonntag, 10. Dezember 2017

ES6 Generator Functions

ES6: ?

There is a new syntax in ES6 for so-called generator-functions. If there is an * (asterisk) behind the keyword function, it is a generator function. The asterisk can be understood as multiplicity, meaning "several values". It can be separated by spaces in any way, like on the following generators *foo(), *bar() and *foobar():

  function* foo() {
    yield "foo"
  }

  function *bar() {
    yield "bar"
  }

  function * foobar() {
    yield* foo()
    yield * bar()
  }
  
  const foobarArray = [ ...foobar() ]
  alert(foobarArray)  // "foo,bar"

A generator function is not a normal function. If you call it, its body is not executed. Instead, an instance of the body is returned, wrapped into an Iterator. Calling the Iterator's next() will iterate the body.

The body must use yield instead of return to output iteration values. The wrapping Iterator will go from yield to yield, until all are done. An optional * (asterisk) behind the yield again stands for multiplicity, i.e. an Iterator is expected to the right of it.

Alternatively to calling next() you can use a for-of loop, apply the spread-operator (like in the example above), or make a destructuring-statement (see examples below).

On the web, you find the sentence

Generators are functions that can be paused and resumed

I think this needs a little more explanation. The Iterator returned from a generator function can go into a wait-for-input state through a yield statement that has nothing on its right side. A next(input) call with a parameter would then move the Iterator to the next yield, whereby the input-parameter is put on the place where the yield was. Should the next yield be an output with a non-empty right side, it will also be performed by this next(input) call.

Let's look at examples to find out what that all means.

ES6 Generator Function Tests

Click onto one of the buttons to get an example script into the text area. Below the script you find an explanation.

  •  
  •  
  •  
  •  
  •  
  •  
Description

Resume

Generator functions come with a lot of abstract words like pausing and resuming, cooperative multitasking, coroutines etc. Looking at the concrete behavior of generator functions may be easier than finding out all about these terms. This is still work in progress. ES7 will introduce an await keyword, so asynchronous processing via generators may change in future.




A generator function does not execute its body when you call it. Instead it returns an instance of its body, wrapped into an Iterator. Each call to the generator function creates a new Iterator instance.

Inside the body, the yield statement must be used instead of return. Should there be a return, the iteration will terminate at that point. Else it will go from one yield to the next, until all are done, always outputting what is right of it.

The example uses a for-of loop to create the test-output.

This example introduces a generator function in a class, and thus the function is called method. The function keyword must be left out (else "SyntaxError: missing : after property id"), so the asterisk looks a little lonely in front of the *keyValueTuples() generator method.

The example class ObjectAnalyzer yields all property / value tuples of an object given in constructor, as an array of two. The for-of loop receives that array in a [ key, value ] destructuring.

When a generator function contains a yield without argument, this waits for input through a next(argument) call. That means, it puts whatever is passed as argument to next() in place of the yield.

This example defines a generator function that has three yields, the first and the second being inputs, the last being an output.

First we must call an empty next() to get to the first yield of the "newborn" iterator. We could call it the "start"-next, any parameter will be ignored. The next("Hello") then goes to the local constant first, and next("World") to second constant. There, at the third call, also the output-yield is done, and we receive "Hello World" from this call.

Try to replace the yield first+" "+second by return first+" "+second. You would fail in the helloWorld.done === true condition, and you would have to change it to helloWorld.done === false. That means, a return triggers done === true, but the last yield does not do such. See also example "End of iteration return".

A yield with a trailing * (asterisk) expects an Iterable to the right of it. That means, the generator function body will stop at the yield statement, output the first value of the Iterable to the right, on next call output the next value, and so on.

In the example, this is shown twice. The arrayIterator() generator uses the yield* to output all elements of a given array parameter. The delegationGenerator() generator uses it to output all values yielded by arrayIterator(). A while-loop is used to create the test-output.

Of course this example is not really useful, but it shows that yield* can go to any depth.

This example tries to make clear the role of a return statement in a nested generator function. The *ab() generator yields "a" and "b", additionally it returns a string "End of iteration return".

An iterator is retrieved from *ab(). When we would destructure or spread it, or loop it in a for-of, we wouldn't get the "End of iteration return", because these instructions ignore the last iteration element that has done === true. We can get the return just when looking at the last iteration element, by reading away all elements with done === false, and then retrieving the value from the last one.

Mind that a return terminates the Iterator, any yield after it makes no sense and will be silently ignored.

This example summarizes the ways how the iterator from a generator function can be iterated. The most elegant way is most likely the for-of loop. The spread-operator does not provide terminating the iteration prematurely, it will always retrieve all elements. Destructuring may not always be the best solution, because it makes assumptions about the return that may not hold. The longest one surely is the while-next loop, but it is the only one that is able to also receive an optional return value.