Monthly Archives: February 2015

Iterables and iterators in ECMAScript 6

By Axel Rauschmayer

ECMAScript 6 introduces a new interface for iteration, Iterable. This blog post explains how it works, which language constructs consume data via it (e.g., the new for-of loop) and which sources provide data via it (e.g., arrays).

Iterability

The idea of iterability is as follows.

  • Data consumers: JavaScript has language constructs that consume data. For example, for-of loops over values and the spread operator (...) inserts values into arrays or function calls.

  • Data sources: The data consumers could get their values from a variety of sources. For example, you may want to iterate over the elements of an array, the key-value entries in a map or the characters of a string.

It’s not practical for every consumer to support all sources, especially because it should be possible to create new sources and consumers, e.g. via libraries with data structures or with new ways of processing data. Therefore, ES6 introduces the interface Iterable. Data consumers use it, data sources implement it:

Given that JavaScript does not have interfaces, Iterable is more of a convention:

  • Source: A value is considered iterable if it has a method whose key is the symbol [2] Symbol.iterator that returns a so-called iterator. The iterator is an object that returns values via its method next(). We say: it enumerates items, one per method call.

  • Consumption: Data consumers use the iterator to retrieve the values they are consuming.

Let’s see what consumption looks like for an array arr. First, you create an iterator via the method whose key is Symbol.iterator:

    > let arr = ['a', 'b', 'c'];
    > let iter = arr[Symbol.iterator]();

Then you call the iterator’s method next() repeatedly to retrieve the items “inside” the array:

    > iter.next()
    { value: 'a', done: false }
    > iter.next()
    { value: 'b', done: false }
    > iter.next()
    { value: 'c', done: false }
    > iter.next()
    { value: undefined, done: true }

As you can see, next() returns each item wrapped in an object, as the value of the property value. The boolean property done indicates when the end of the sequence of items has been reached.

Iterable and iterators are part of a so-called protocol (methods plus rules for using them) for iteration. A key characteristic of this protocol is that it is sequential: the iterator returns values one at a time. That means that if an iterable data structure is non-linear (such as a tree), iteration will linearize it.

Iterable data sources

I’ll use the for-of loop (which is explained in more detail later) to iterate over various kinds of iterable data.

Arrays

Arrays (and typed arrays) are iterables over their elements:

    for (let x of ['a', 'b']) {
        console.log(x);
    }
    // Output:
    // 'a'
    // 'b'

Strings

Strings are iterable, but they enumerate Unicode code points, each of which may comprise one or two JavaScript “characters”:

    for (let x of 'auD83DuDC0A') {
        console.log(x);
    }
    // Output:
    // 'a'
    // 'uD83DuDC0A' (crocodile emoji)

Note that you have just seen that primitive values can be iterable, too. A value doesn’t have to be an object in order to be iterable.

Maps

Maps [3] are iterables over their entries. Each entry is encoded as a [key, value] pair, an array with two elements. The entries are always enumerated deterministically, in the same order in which they were added to the map.

    let map = new Map().set('a', 1).set('b', 2);
    for (let pair of map) {
        console.log(pair);
    }
    // Output:
    // ['a', 1]
    // ['b', 2]

Note that WeakMaps [3] are not iterable.

Sets

Sets [3] are iterables over their elements (which are enumerated in the same order in which they were added to the set).

    let set = new Set().add('a').add('b');
    for (let x of set) {
        console.log(x);
    }
    // Output:
    // 'a'
    // 'b'

Note that WeakSets [3] are not iterable.

arguments

Even though the special variable arguments is more or less obsolete in ECMAScript 6 (due to rest parameters), it is iterable:

    function printArgs() {
        for (let x of arguments) {
            console.log(x);
        }
    }
    printArgs('a', 'b');
    
    // Output:
    // 'a'
    // 'b'

DOM data structures

Most DOM data structures will eventually be iterable:

    for (let node of document.querySelectorAll('···')) {
        ···
    }

Note that implementing this functionality is work in progress. But it is relatively easy to do so, because the symbol Symbol.iterator can’t clash with existing property keys [2].

Iterable computed data

Not all iterable content does have to come from data structures, it could also be computed on the fly. For example, all major ES6 data structures (arrays, typed arrays, maps, sets) have three methods that return iterable objects:

  • entries() returns an iterable over entries encoded as [key,value] arrays. For arrays, the values are the array elements and the keys are their indices. For sets, each key and value are the same – the set element.
  • keys() returns an iterable over the keys of the entries.
  • values() returns an iterable over the values of the entries.

Let’s see what that looks like. entries() gives you a nice way to get both array elements and their indices:

    let arr = ['a', 'b', 'c'];
    for (let pair of arr.entries()) {
        console.log(pair);
    }
    // Output:
    // [0, 'a']
    // [1, 'b']
    // [2, 'c']

Plain objects are not iterable

Plain objects (as created by object literals) are not iterable:

    for (let x of {}) { // TypeError
        console.log(x);
    }

The reasoning is as follows. The following two activities are different:

  1. Examining the structure of a program (reflection)
  2. Iterating over data

It is best to keep these two activities separate. #1 is relevant for all objects, #2 only for data structures. You could make most objects iterable by adding a method [Symbol.iterator]() to Object.prototype, but they would lose this ability in two cases:

  • If they are created via Object.create(null). Then Object.prototype is not in their prototype chain.
  • If they are data structures. Then they need iterability for their data. Not only would you not be able to iterate over the properties of, say, arrays (which are also data structures). But you couldn’t ever later add iterability to an existing class, because that would break code that iterates over the properties of their instances.

Therefore, the safest way to make properties iterable is via a tool function. For example, via objectEntries(), whose implementation is shown later (future ECMAScript versions may have something similar built in):

    let obj = { first: 'Jane', last: 'Doe' };
    
    for (let [key,value] of objectEntries(obj)) {
        console.log(`${key}: ${value}`);
    }
    
    // Output:
    // first: Jane
    // last: Doe

It is also important to remember that iterating over the properties of an object is mainly interesting if you use objects as maps [4]. But we only do that in ES5 because we have no better alternative. In ECMAScript 6, we have Map.

Iterating language constructs

This section lists all built-in ES6 programming constructs that make use of the iteration protocol.

Destructuring via an array pattern

Destructuring [5] via array patterns works for any iterable:

    let set = new Set().add('a').add('b').add('c');
    
    let [x,y] = set;
        // x='a'; y='b'
    
    let [first, ...rest] = set;
        // first='a'; rest=['b','c'];

The for-of loop

for-of is a new loop in ECMAScript 6. One form of it looks like this:

    for (let x of iterable) {
        ···
    }

This loop iterates over iterable, assigns each of the enumerated items to the iteration variable x and lets you process it in the body. The scope of x is the loop, it doesn’t exist outside it.

Note that the iterability of iterable is required, otherwise for-of can’t loop over a value. That means that non-iterable values must be converted to something iterable. For example, via Array.from(), which turns array-like values and iterables into arrays:

    let arrayLike = { length: 2, 0: 'a', 1: 'b' };
    
    for (let x of arrayLike) { // TypeError
        console.log(x);
    }
    
    for (let x of Array.from(arrayLike)) { // OK
        console.log(x);
    }

I expect for-of to mostly replace Array.prototype.forEach(), because it is more versatile (forEach() only works for array-like values) and will be faster long term (see FAQ at the end).

Iteration variables: let declarations vs. var declarations

If you let-declare the iteration variable, a fresh binding (slot) will be created for each iteration. That can be seen in the following code snippet where we save the current binding of elem for later, via an arrow function. Afterwards, you can see that the arrow functions don’t share the same binding for elem, they each have a different one.

    let arr = [];
    for (let elem of [0, 1, 2]) {
        arr.push(() => elem); // save `elem` for later
    }
    console.log(arr.map(f => f())); // [0, 1, 2]
    
    // `elem` only exists inside the loop:
    console.log(elem); // ReferenceError: elem is not defined

It is instructive to see how things are different if you var-declare the iteration variable. Now all arrow functions refer to the same binding of elem.

    let arr = [];
    for (var elem of [0, 1, 2]) {
        arr.push(() => elem);
    }
    console.log(arr.map(f => f())); // [2, 2, 2]
    
    // `elem` exists in the surrounding function:
    console.log(elem); // 2

Having one binding per iteration is very helpful whenever you create functions via a loop (e.g. to add event listeners).

let-declared iteration variables in for loops and for-in loops

Two more loops get one binding per iteration if you let-declare their iteration variables: for and for-in.

Let’s look at for with a let-declared iteration variable i:

    let arr = [];
    for (let i=0; i<3; i++) {
        arr.push(() => i);
    }
    console.log(arr.map(f => f())); // [0, 1, 2]
    console.log(i); // ReferenceError: i is not defined

If you var-declare i, you get the traditional behavior.

    let arr = [];
    for (var i=0; i<3; i++) {
        arr.push(() => i);
    }
    console.log(arr.map(f => f())); // [3, 3, 3]
    console.log(i); // 3

Similarly, for-in with a let-declared iteration variable key leads to one binding per iteration:

    let arr = [];
    for (let key in ['a', 'b', 'c']) {
        arr.push(() => key);
    }
    console.log(arr.map(f => f())); // ['0', '1', '2']
    console.log(key); // ReferenceError: key is not defined

var-declaring key produces a single binding:

    let arr = [];
    for (var key in ['a', 'b', 'c']) {
        arr.push(() => key);
    }
    console.log(arr.map(f => f())); // ['2', '2', '2']
    console.log(key); // '2'
Iterating with existing variables, object properties and array elements

So far, we have only seen for-of with a declared iteration variable. But there are several other forms.

You can iterate with an existing variable:

    let x;
    for (x of ['a', 'b']) {
        console.log(x);
    }

You can also iterate with an object property:

    let obj = {};
    for (obj.prop of ['a', 'b']) {
        console.log(obj.prop);
    }

And you can iterate with an array element:

    let arr = [];
    for (arr[0] of ['a', 'b']) {
        console.log(arr[0]);
    }
Iterating with a destructuring pattern

Combining for-of with destructuring is especially useful for iterables over key-value pairs (encoded as arrays). That’s what maps are:

    let map = new Map().set(false, 'no').set(true, 'yes');
    for (let [k,v] of map) {
        console.log(`key = ${k}, value = ${v}`);
    }
    // Output:
    // key = false, value = no
    // key = true, value = yes

Array.prototype.entries() also returns an iterable over key-value pairs:

    let arr = ['a', 'b', 'c'];
    for (let [k,v] of arr.entries()) {
        console.log(`key = ${k}, value = ${v}`);
    }
    // Output:
    // key = 0, value = a
    // key = 1, value = b
    // key = 2, value = c

Therefore, entries() gives you a way to treat enumerated items differently, depending on their position:

    /** Same as arr.join(', ') */
    function toString(arr) {
        let result = '';
        for (let [i,elem] of arr.entries()) {
            if (i > 0) {
                result += ', ';
            }
            result += String(elem);
        }
        return result;
    }

This function is used as follows:

    > toString(['eeny', 'meeny', 'miny', 'moe'])
    'eeny, meeny, miny, moe'

Array.from()

Array.from() [6] converts iterable and array-like values to arrays. It is also available for typed arrays.

    > Array.from(new Map().set(false, 'no').set(true, 'yes'))
    [[false,'no'], [true,'yes']]
    > Array.from({ length: 2, 0: 'hello', 1: 'world' })
    ['hello', 'world']

Array.from() works as expected for a subclass of Array (which inherits this class method) – it converts iterables to instances of the subclass.

Spread

The spread operator [5] inserts the values of an iterable into an array:

    > let arr = ['b', 'c'];
    > ['a', ...arr, 'd']
    ['a', 'b', 'c', 'd']

That means that it provides you with a compact way to convert any iterable to an array:

    let arr = [...iterable];

The spread operator also turns an iterable into the arguments of a function, method or constructor call:

    > Math.max(...[-1, 8, 3])
    8

Maps and sets

The constructor of a map turns an iterable over [key,value] pairs into a map:

    > let map = new Map([['uno', 'one'], ['dos', 'two']]);
    > map.get('uno')
    'one'
    > map.get('dos')
    'two'

The constructor of a set turns an iterable over elements into a set:

    > let set = new Set(['red', 'green', 'blue']);
    > set.has('red')
    true
    > set.has('yellow')
    false

The constructors of WeakMap and WeakSet work similarly. Furthermore, maps and sets are iterable themselves (WeakMaps and WeakSets aren’t), which means that you can use their constructors to clone them.

Promises

Promise.all() and Promise.race() accept iterables over promises [7]:

    Promise.all(iterableOverPromises).then(···);
    Promise.race(iterableOverPromises).then(···);

yield*

yield* [8] yields all items enumerated by an iterable.

    function* yieldAllValuesOf(iterable) {
        yield* iterable;
    }

The most important use case for yield* is to recursively call a generator [8] (which produces something iterable).

Implementing iterables

The iteration protocol looks as follows.

An object becomes iterable (“implements” the interface Iterable) if it has a method (own or inherited) whose key is Symbol.iterator. That method must return an iterator, an object that enumerates the items “inside” the iterable via its method next().

In TypeScript notation, the interfaces for iterables and iterators look as follows (based on [9]):

    interface Iterable {
        [System.iterator]() : Iterator,
    }
    interface IterationResult {
        value: any,
        done: boolean,
    }
    interface Iterator {
        next(value?: any) : IterationResult,
        return?(value?: any) : IterationResult,
        throw?(value?: any) : IterationResult,
    }

return and throw are optional methods that we’ll get to later. Let’s first implement a dummy iterable to get a feeling for how iteration works.

    let iterable = {
        [Symbol.iterator]() {
            let step = 0;
            let iterator = {
                next() {
                    if (step <= 2) {
                        step++;
                    }
                    switch (step) {
                        case 1:
                            return { value: 'hello', done: false };
                        case 2:
                            return { value: 'world', done: false };
                        default:
                            return { value: undefined, done: true };
                    }
                }
            };
            return iterator;
        }
    };

Let’s check that iterable is, in fact, iterable:

    for (let x of iterable) {
        console.log(x);
    }
    // Output:
    // hello
    // world

The code executes three steps, with the counter step ensuring that everything happens in the right order. First we, return the value 'hello', then the value 'world' and then we indicate that the end of the enumerated items has been reached. Each item is wrapped in an object with the properties:

  • value which holds the actual item and
  • done which is a boolean flag that indicates whether the end has been reached, yet.

You can omit done if it is false and value if it is undefined. That is, the switch statement could be written as follows.

    switch (step) {
        case 1:
            return { value: 'hello' };
        case 2:
            return { value: 'world' };
        default:
            return { done: true };
    }

As will be explained in a follow-up blog post on generators, there are cases where you want even the last item with done: true to have a value. Otherwise, next() could be simpler and return items directly (without wrapping them in objects). The end of iteration would then be indicated via a special value (e.g., a symbol).

Let’s look at one more implementation of an iterable. The function iterateOver() returns an iterable over the arguments that are passed to it:

    function iterateOver(...args) {
        let index = 0;
        let iterable = {
            [Symbol.iterator]() {
                let iterator = {
                    next() {
                        if (index < args.length) {
                            return { value: args[index++] };
                        } else {
                            return { done: true };
                        }
                    }
                };
                return iterator;
            }
        }
        return iterable;
    }
    
    // Using `iterateOver()`:
    for (let x of iterateOver('fee', 'fi', 'fo', 'fum')) {
        console.log(x);
    }
    
    // Output:
    // fee
    // fi
    // fo
    // fum

Iterators that are iterable

The previous function can be simplified if the iterable and the iterator are the same object:

    function iterateOver(...args) {
        let index = 0;
        let iterable = {
            [Symbol.iterator]() {
                return this;
            },
            next() {
                if (index < args.length) {
                    return { value: args[index++] };
                } else {
                    return { done: true };
                }
            },
        };
        return iterable;
    }

Even if the original iterable and the iterator are not the same object, it is still occasionally useful if an iterator has the following method (which also makes it an iterable):

    [Symbol.iterator]() {
        return this;
    }

All built-in ES6 iterators follow this pattern (via a common prototype, see follow-up blog post on generators). For example, the default iterator for arrays:

    > let arr = [];
    > let iterator = arr[Symbol.iterator]();
    > iterator[Symbol.iterator]() === iterator
    true

Why is it useful if an iterator is also an iterable? for-of only works for iterables, not for iterators. Because array iterators are iterable, you can continue an iteration in another loop:

    let arr = ['a', 'b'];
    let iterator = arr[Symbol.iterator]();
    
    for (let x of iterator) {
        console.log(x); // a
        break;
    }
    
    // Continue with same iterator:
    for (let x of iterator) {
        console.log(x); // b
    }

An alternative is to use a method that returns an iterable. For example, the result of Array.prototype.values() iterates the same way as the default iteration. Therefore, the previous code snippet is equivalent to:

    let arr = ['a', 'b'];
    let iterable = arr.values();
    for (let x of iterable) {
        console.log(x); // a
        break;
    }
    for (let x of iterable) {
        console.log(x); // b
    }

But with an iterable, you can’t be sure that it won’t restart iteration if for-of calls the method [Symbol.iterator](). For example, instances of Array are iterables that start at the beginning whenever you call this method.

One use case for continuing an iteration is that you can remove initial items (e.g. a header) before processing the actual content via for-of.

Optional iterator methods: return() and throw()

Two iterator methods are optional:

  • return() gives an iterator the opportunity to clean up if an iteration ends prematurely.
  • throw() is about forwarding a method call to a generator that is iterated over via yield*. It is explained in a follow-up blog post on generators.
The optional iterator method return()

As mentioned before, the optional iterator method return() is about letting an iterator clean up if it wasn’t iterated over until the end. It closes an iterator. In for-of loops, premature (or abrupt, in spec language) termination can be caused by:

  • break
  • continue (if you continue an outer loop, continue acts like a break)
  • throw
  • return

In each of these cases, for-of lets the iterator know that the loop won’t finish. Let’s look at an example, a function readLinesSync that returns an iterable of text lines in a file and would like to close that file no matter what happens:

    function readLinesSync(fileName) {
        let file = ···;
        return {
            ···
            next() {
                if (file.isAtEndOfFile()) {
                    file.close();
                    return { done: true };
                }
                ···
            },
            return() {
                file.close();
                return { done: true };
            },
        };
    }

Due to return(), the file will be properly closed in the following loop:

    // Only print first line
    for (let line of readLinesSync(fileName)) {
        console.log(x);
        break;
    }

The return() method must return an object. That is due to how generators handle the return statement and will be explained in the follow-up blog post on generators.

The following constructs close iterators if aren’t completely “drained”:

  • for-of
  • yield*
  • Destructuring
  • Array.from()
  • Map(), Set(), WeakMap(), WeakSet()
  • Promise.all(), Promise.race()

More examples of iterables

In this section, we look at a few more examples of iterables. Most of these iterables are easier to implement via generators. A follow-up blog post will show how.

Tool functions that return iterables

Tool functions and methods that return iterables are just as important as iterable data structures. The following is a tool function for iterating over the own properties of an object.

    function objectEntries(obj) {
        let index = 0;
    
        // In ES6, you can use strings or symbols as property keys,
        // Reflect.ownKeys() retrieves both
        let propKeys = Reflect.ownKeys(obj);
    
        return {
            [Symbol.iterator]() {
                return this;
            },
            next() {
                if (index < propKeys.length) {
                    let key = propKeys[index];
                    index++;
                    return { value: [key, obj[key]] };
                } else {
                    return { done: true };
                }
            }
        };
    }
    
    let obj = { first: 'Jane', last: 'Doe' };
    
    for (let [key,value] of objectEntries(obj)) {
        console.log(`${key}: ${value}`);
    }
    
    // Output:
    // first: Jane
    // last: Doe

Combinators for iterables

Combinators [10] are functions that combine existing iterables to create new ones.

Let’s start with the combinator function take(n, iterable), which returns an iterable over the first n items of iterable.

    function take(n, iterable) {
        let iter = iterable[Symbol.iterator]();
        return {
            [Symbol.iterator]() {
                return this;
            },
            next() {
                if (n > 0) {
                    n--;
                    return iter.next();
                } else {
                    return { done: true };
                }
            }
        };
    }
    let arr = ['a', 'b', 'c', 'd'];
    for (let x of take(2, arr)) {
        console.log(x);
    }
    // Output:
    // a
    // b

zip turns n iterables into an iterable of n-tuples (encoded as arrays of length n).

    function zip(...iterables) {
        let iterators = iterables.map(i => i[Symbol.iterator]());
        let done = false;
        return {
            [Symbol.iterator]() {
                return this;
            },
            next() {
                if (!done) {
                    let items = iterators.map(i => i.next());
                    done = items.some(item => item.done);
                    if (!done) {
                        return { value: items.map(i => i.value) };
                    }
                    // Done for the first time: close all iterators
                    for (let iterator of iterators) {
                        iterator.return();
                    }
                }
                // We are done
                return { done: true };
            }
        }
    }

As you can see, the shortest iterable determines the length of the result:

    let zipped = zip(['a', 'b', 'c'], ['d', 'e', 'f', 'g']);
    for (let x of zipped) {
        console.log(x);
    }
    // Output:
    // ['a', 'd']
    // ['b', 'e']
    // ['c', 'f']

Infinite iterables

Some iterable may never be done.

    function naturalNumbers() {
        let n = 0;
        return {
            [Symbol.iterator]() {
                return this;
            },
            next() {
                return { value: n++ };
            }
        }
    }

With an infinite iterable, you must not iterate over “all” of it. For example, by breaking from a for-of loop:

    for (let x of naturalNumbers()) {
        if (x > 2) break;
        console.log(x);
    }

Or by only accessing the beginning of an infinite iterable:

    let [a, b, c] = naturalNumbers();
        // a=0; b=1; c=2;

Or by using a combinator. take() is one possibility:

    for (let x of take(3, naturalNumbers())) {
        console.log(x);
    }
    // Output:
    // 0
    // 1
    // 2

The “length” of the iterable returned by zip() is determined by its shortest input iterable. That means that zip() and naturalNumbers() provide you with the means to number iterables of arbitrary (finite) length:

    let zipped = zip(['a', 'b', 'c'], naturalNumbers());
    for (let x of zipped) {
        console.log(x);
    }
    // Output:
    // ['a', 0]
    // ['b', 1]
    // ['c', 2]

Frequently asked question

Isn’t the iteration protocol slow?

You may be worried about the iteration protocol being slow, because a new object is created for each invocation of next(). However, memory management for small objects is fast in modern engines and in the long run, engines can optimize iteration so that no intermediate objects need to be allocated. A thread on es-discuss has more information.

Conclusion

In this blog post we have seen that even with just the foundations of ES6 iteration, you can already do a lot. Generators [8] build on that foundation and help with, among other things, implementing iterators.

The JavaScript runtime library is still missing tools for working with iterators. Python has the feature-rich module itertools, JavaScript will eventually get a similar module.

Further reading

  1. Exploring ES6: Upgrade to the next version of JavaScript”, book by Axel
  2. Symbols in ECMAScript 6
  3. ECMAScript 6: maps and sets
  4. Pitfalls: Using an Object as a Map” in “Speaking JavaScript”
  5. Destructuring and parameter handling in ECMAScript 6 [includes an explanation of the spread operator (...)]
  6. ECMAScript 6’s new array methods
  7. ECMAScript 6 promises (2/2): the API
  8. I’ll soon publish a new blog post on generators. Until then, you can read this one: “Iterators and generators in ECMAScript 6
  9. Closing iterators”, slides by David Herman
  10. Combinator” in HaskellWiki

Source:: 2ality

Freebie: 7 Pretty and Responsive Header Templates

By Danny Markov

5-beautiful-responsive-header-templates

Last time we shared with you a nice collection of free and responsive footer templates. To make the package complete, today we’ve got for you 7 responsive headers that you can download, copy and paste in your web designs straight away. The headers feature a company logo, navigation links and other goodies like search boxes and login buttons.

The Design

All of the headers are responsive and retina ready and should look nice on any device and resolution. Each header design has it’s own CSS file, which makes it very easy to use and customize. The CSS is self-contained so there isn’t any risk of it breaking the styles for the rest of your page. Some of the templates come with short JavaScript snippets, which are jQuery dependent, so make sure to copy them together with a link to jQuery via CDN or local file.

Pretty and Responsive Responsive Header Templates

Free for Commercial Use

These templates are free to use and you can make them part of your commercial or non-commercial designs. You can check our license for more info. We hope that you like them!

Source:: Tutorialzine.com

Smartphone Remote Control with Node.js and Socket.io

By Nick Anastasov

smartphone-remote-control-for-your-presentation

Wouldn’t it be cool to use your smartphone as a remote control? It turns out it is not difficult at all! You don’t even need to know how to write native mobile apps – your phone has a fully capable web browser with support for web sockets, which opens a lot of possibilities. In this short tutorial, we are going to use Node.js and Socket.io to remotely control a presentation that is running on your laptop with your phone.

There are a lot of cool HTML5 presentation libraries out there and it would be a waste of effort to create this functionality from scratch. This is why we will be using Reveal.js – it will handle animations and transitions between slides, as well as support for keyboard and touch events.

We won’t be making a dedicated remote control interface. Instead, we will synchronize the presentation that is opened on your phone with that on your computer using websockets. This will allow you not only to control the presentation, but see its mobile version on your phone, which will help you keep track of the current slide.

The Idea

The technique we are going to use is very simple. Reveal.js puts the current slide number in the URL as a hash (e.g. http://example.com/#/1). We will send this hash to all other connected devices, which will transition them to the new slide automatically. This will make it possible for people to just open the URL in a browser and sabotaging your presentation, so we will require all devices to enter a pass code before connecting.

It is worth mentioning that Reveal.js already has an API, so we could have used that to synchronize the two presentations. But the hash change technique is simpler and will work with any presentation library, so we chose to go with it instead.

Our slideshow

Running our Example

You can run this example locally, or by deploying it to a hosting provider with node.js support like Heroku. Running it locally is easier, but you must have node.js and npm installed. Running it on Heroku requires you to have the heroku toolbelt installed and signing up for an account.

To run our code locally:

  1. Download the code from the button near the beginning of the article.
  2. Make sure you have node.js installed. If needed, install it.
  3. Unzip the archive you downloaded to a folder.
  4. Open a terminal and cd to the folder.
  5. Run npm install to install the required libraries
  6. Run node app.js to start the presentation
  7. Open http://localhost:8080 on your computer and enter your pass key (by default it is “kittens“).
  8. Open http:// on your phone and enter the same pass key.
  9. Have fun!

To run the code on Heroku:

  1. Download the code from the button near the beginning of the article.
  2. Unzip it to a folder.
  3. Open a terminal and cd to the folder.
  4. Create a git repository, and commit.
  5. Create a new Heroku App
  6. Run git push heroku master.
  7. Visit the URL of the app on every device you want to connect. The default pass key is “kittens”.

Read more about deploying node.js apps to heroku here. If you use a different cloud hosting provider, the last three steps will be different.

The Code

But enough talk, let’s see the code! There are only two JavaScript files – app.js for the server side, and script.js for the browser. You can run the app in Node.js 0.10+ or in io.js.

For the backend, we use express and socket.io. It’s main responsibility is listening for and responding to socket.io events. With express.static, we serve the files in the public folder to the world. We have a public/index.html file which contains the code for the presentation. It is served automatically by express.static, so we don’t need a “/” route.

app.js

// This is the server-side file of our mobile remote controller app.
// It initializes socket.io and a new express instance.
// Start it by running 'node app.js' from your terminal.


// Creating an express server

var express = require('express'),
	app = express();

// This is needed if the app is run on heroku and other cloud providers:

var port = process.env.PORT || 8080;

// Initialize a new socket.io object. It is bound to 
// the express app, which allows them to coexist.

var io = require('socket.io').listen(app.listen(port));


// App Configuration

// Make the files in the public folder available to the world
app.use(express.static(__dirname + '/public'));


// This is a secret key that prevents others from opening your presentation
// and controlling it. Change it to something that only you know.

var secret = 'kittens';

// Initialize a new socket.io application

var presentation = io.on('connection', function (socket) {

	// A new client has come online. Check the secret key and 
	// emit a "granted" or "denied" message.

	socket.on('load', function(data){

		socket.emit('access', {
			access: (data.key === secret ? "granted" : "denied")
		});

	});

	// Clients send the 'slide-changed' message whenever they navigate to a new slide.

	socket.on('slide-changed', function(data){

		// Check the secret key again

		if(data.key === secret) {

			// Tell all connected clients to navigate to the new slide
			
			presentation.emit('navigate', {
				hash: data.hash
			});
		}
	});
});

console.log('Your presentation is running on http://localhost:' + port);

And here is our JavaScript for the front-end, which listens for hashchange events and sends socket.io messages to the server.

public/assets/js/script.js

$(function() {

	// Apply a CSS filter with our blur class (see our assets/css/styles.css)
	
	var blurredElements = $('.homebanner, div.reveal').addClass('blur');

	// Initialize the Reveal.js library with the default config options
	// See more here https://github.com/hakimel/reveal.js#configuration

	Reveal.initialize({
		history: true		// Every slide will change the URL
	});

	// Connect to the socket

	var socket = io();

	// Variable initialization

	var form = $('form.login'),
		secretTextBox = form.find('input[type=text]');

	var key = "", animationTimeout;

	// When the page is loaded it asks you for a key and sends it to the server

	form.submit(function(e){

		e.preventDefault();

		key = secretTextBox.val().trim();

		// If there is a key, send it to the server-side
		// through the socket.io channel with a 'load' event.

		if(key.length) {
			socket.emit('load', {
				key: key
			});
		}

	});

	// The server will either grant or deny access, depending on the secret key

	socket.on('access', function(data){

		// Check if we have "granted" access.
		// If we do, we can continue with the presentation.

		if(data.access === "granted") {

			// Unblur everything
			blurredElements.removeClass('blurred');

			form.hide();

			var ignore = false;

			$(window).on('hashchange', function(){

				// Notify other clients that we have navigated to a new slide
				// by sending the "slide-changed" message to socket.io

				if(ignore){
					// You will learn more about "ignore" in a bit
					return;
				}

				var hash = window.location.hash;

				socket.emit('slide-changed', {
					hash: hash,
					key: key
				});
			});

			socket.on('navigate', function(data){
	
				// Another device has changed its slide. Change it in this browser, too:

				window.location.hash = data.hash;

				// The "ignore" variable stops the hash change from
				// triggering our hashchange handler above and sending
				// us into a never-ending cycle.

				ignore = true;

				setInterval(function () {
					ignore = false;
				},100);

			});

		}
		else {

			// Wrong secret key

			clearTimeout(animationTimeout);

			// Addding the "animation" class triggers the CSS keyframe
			// animation that shakes the text input.

			secretTextBox.addClass('denied animation');
			
			animationTimeout = setTimeout(function(){
				secretTextBox.removeClass('animation');
			}, 1000);

			form.show();
		}

	});

});

It’s Slideshow Time!

With this our smartphone remote control is ready! We hope that you find our experiment useful and have fun playing with it.

Source:: Tutorialzine.com

Classes in ECMAScript 6 (final semantics)

By Axel Rauschmayer

Recently, TC39 decided on the final semantics of classes in ECMAScript 6 [2]. This blog post explains how their final incarnation works. The most significant recent changes were related to how subclassing is handled.

Overview

    class Point {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
        toString () {
            return '(' + this.x + ', ' + this.y + ')';
        }
    }
    
    class ColorPoint extends Point {
        constructor(x, y, color) {
            super(x, y);
            this.color = color;
        }
        toString () {
            return super.toString() + ' in ' + this.color;
        }
    }
    
    let cp = new ColorPoint(25, 8, 'green');
    cp.toString(); // '(25, 8) in green'
    
    console.log(cp instanceof ColorPoint); // true
    console.log(cp instanceof Point); // true

The essentials

Base classes

A class is defined like this in ECMAScript 6 (ES6):

    class Point {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
        toString () {
            return '(' + this.x + ', ' + this.y + ')';
        }
    }

You use this class just like an ES5 constructor functions:

    > var p = new Point(25, 8);
    > p.toString()
    '(25, 8)'

In fact, classes internally produce constructors. That is, the ES6 class Point is roughly equivalent to the following ES5 code:

    function Point(x, y) {
        this.x = x;
        this.y = y;
    }
    Point.prototype.toString = function () {
        return '(' + this.x + ', ' + this.y + ')';
    };

A class body can only contain methods, no data properties. Prototypes having data properties is generally considered an anti-pattern, so this just enforces a best practice.

Class declarations are not hoisted

Function declarations are hoisted: When entering a scope, the functions that are declared in it are immediately available – independently of where the declarations happen. That means that you can call a function that is declared later:

    foo(); // works, because `foo` is hoisted
    
    function foo() {}

In contrast, class declarations are not hoisted. Therefore, a class only exists after execution reached its definition and it was evaluated. Accessing it beforehand leads to a ReferenceError:

    new Foo(); // ReferenceError
    
    class Foo {}

The reason for this limitation is that classes can have an extends clause whose value is an arbitrary expression. That expression must be evaluated in the proper “location”, its evaluation can’t be hoisted.

Not having hoisting is less limiting than you may think. For example, a function that comes before a class declaration can still refer to that class, but you have to wait until the class declaration has been evaluated before you can call the function.

    function functionThatUsesBar() {
        new Bar();
    }
    
    functionThatUsesBar(); // ReferenceError
    class Bar {}
    functionThatUsesBar(); // OK

Getters and setters

The syntax for getters and setters is just like in ECMAScript 5 object literals:

    class MyClass {
        get prop() {
            return 'getter';
        }
        set prop(value) {
            console.log('setter: '+value);
        }
    }

You use MyClass as follows.

    > let inst = new MyClass();
    > inst.prop = 123;
    setter: 123
    > inst.prop
    'getter'

Generator methods

If you prefix a method definition with an asterisk (*), it becomes a generator method [3]. Among other things, a generator is useful for defining the method whose key is Symbol.iterator. That method makes objects iterable [3], which means that they can be iterated over via the for-of loop and other ES6 language mechanisms. The following code demonstrates how that works.

    class IterableArguments {
        constructor(...args) {
            this.args = args;
        }
        * [Symbol.iterator]() {
            for (let arg of this.args) {
                yield arg;
            }
        }
    }
    
    for (let x of new IterableArguments('hello', 'world')) {
        console.log(x);
    }
    
    // Output:
    // hello
    // world

Class expressions

Similarly to functions, there are both class declarations and class expressions. Also similarly, the identifier of a class expression is only visible within the expression:

    const MyClass = class Me {
        getClassName() {
            return Me.name;
        }
    };
    let inst = new MyClass();
    console.log(inst.getClassName()); // Me
    console.log(Me.name); // ReferenceError: Me is not defined

Static properties

Given a class Foo, we distinguish two kinds of properties:

  • The prototype properties of Foo are the properties of Foo.prototype. They are usually methods and inherited by instances of Foo.
  • The static properties (or class properties) of Foo are the properties of Foo itself.

The prototype properties defined inside the body of a class are all methods. If you prefix one of those method definitions with the keyword static, you create a class method:

    class Foo {
        static classMethod() {
            return 'hello';
        }
    }
    Foo.classMethod(); // 'hello'

Subclassing

The extends clause lets you create a subclass of an existing constructor (which may or may not have been defined via a class):

    class Point {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
        toString () {
            return '(' + this.x + ', ' + this.y + ')';
        }
    }
    
    class ColorPoint extends Point {
        constructor(x, y, color) {
            super(x, y);
            this.color = color;
        }
        toString () {
            return super.toString() + ' in ' + this.color;
        }
    }

Again, this class is used like you’d expect:

    > let cp = new ColorPoint(25, 8, 'green');
    > cp.toString()
    '(25, 8) in green'
    
    > cp instanceof ColorPoint
    true
    > cp instanceof Point
    true

Point is called a base class, because it doesn’t have an extends clause. ColorPoint is called a derived class.

The prototype of a subclass is the superclass

The prototype of a subclass is the superclass in ECMAScript 6:

    > Object.getPrototypeOf(ColorPoint) === Point
    true

That means that static properties are inherited:

    class Foo {
        static classMethod() {
            return 'hello';
        }
    }
    
    class Bar extends Foo {
    }
    Bar.classMethod(); // 'hello'
Super-constructor calls

In a derived class, you must call super() before you can use this:

    class Foo {}
    
    class Bar extends Foo {
        constructor(num) {
            let tmp = num * 2; // OK
            this.num = num; // ReferenceError
            super();
            this.num = num; // OK
        }
    }

Implicitly leaving a derived constructor without calling super() also causes an error:

    class Foo {}
    
    class Bar extends Foo {
        constructor() {
        }
    }
    
    let bar = new Bar(); // ReferenceError
Default constructors for classes

If you don’t specify a constructor for a base class, the following definition is used:

    constructor() {}

For derived classes, the following default constructor is used:

    constructor(...args) {
        super(...args);
    }

Subclassing built-in constructors

In ECMAScript 6, you can finally subclass all built-in constructors (there are work-arounds for ES5, but these have significant limitations).

For example, you can now create your own exception classes (that will inherit the feature of having a stack trace in most engines):

    class MyError extends Error {    
    }
    throw new MyError('Something happened!');

You can also create subclasses of Array whose instances properly handle length:

    class MyArray extends Array {
        constructor(len) {
            super(len);
        }
    }
    
    // Instances of of `MyArray` work like real arrays:
    let myArr = new MyArray(0);
    console.log(myArr.length); // 0
    myArr[0] = 'foo';
    console.log(myArr.length); // 1

Note that subclassing built-in constructors is something that engines have to support natively, you won’t get this feature via transpilers.

Overriding the result of a constructor

Just like in ES5, you can override the result of a constructor by explicitly returning an object:

    class Foo {
        constructor() {
            return Object.create(null);
        }
    }
    console.log(new Foo() instanceof Foo); // false

The details of classes

What we have seen so far are the essentials of classes. You only need to read on if you are interested how things happen under the hood. Let’s start with the syntax of classes. The following is a slightly modified version of the syntax shown in Sect. A.4 of the ECMAScript 6 specification.

    ClassDeclaration:
        "class" BindingIdentifier ClassTail
    ClassExpression:
        "class" BindingIdentifier? ClassTail
    
    ClassTail:
        ClassHeritage? "{" ClassBody? "}"
    ClassHeritage:
        "extends" AssignmentExpression
    ClassBody:
        ClassElement+
    ClassElement:
        MethodDefinition
        "static" MethodDefinition
        ";"
    
    MethodDefinition:
        PropName "(" FormalParams ")" "{" FuncBody "}"
        "*" PropName "(" FormalParams ")" "{" GeneratorBody "}"
        "get" PropName "(" ")" "{" FuncBody "}"
        "set" PropName "(" PropSetParams ")" "{" FuncBody "}"
    
    PropertyName:
        LiteralPropertyName
        ComputedPropertyName
    LiteralPropertyName:
        IdentifierName  /* foo */
        StringLiteral   /* "foo" */
        NumericLiteral  /* 123.45, 0xFF */
    ComputedPropertyName:
        "[" Expression "]"

Two observations:

  • The value to be extended can be produced by an arbitrary expression. Which means that you’ll be able to write code such as the following:
          class Foo extends combine(MyMixin, MySuperClass) {}
    
  • Semicolons are allowed between methods.

Various checks

  • Error checks: the class name cannot be eval or arguments; duplicate class element names are not allowed; the name constructor can only be used for a normal method, not for a getter, a setter or a generator method.
  • Classes can’t be function-called. They throw a TypeException if they are.
  • Prototype methods cannot be used as constructors:
          class C {
              m() {}
          }
          new C.prototype.m(); // TypeError
    

Attributes of properties

Class declarations create (mutable) let bindings. For a given class Foo:

  • Static methods Foo.* are writable and configurable, but not enumerable. Making them writable allows for dynamic patching.
  • A constructor and the object in its property prototype have an immutable link:
    • Foo.prototype is non-writeable, non-enumerable, non-configurable.
    • Foo.prototype.constructor is non-writeable, non-enumerable, non-configurable.
  • Prototype methods Foo.prototype.* are writable and configurable, but not enumerable.

Note that method definitions in object literals produce enumerable properties.

The details of subclassing

Subclassing in ECMAScript 5

Let’s review how one does subclassing in ECMAScript 5:

    function Point(x, y) {
        this.x = x;
        this.y = y;
    }
    ···
    
    // Allocate instance here
    function ColorPoint(x, y, color) {
        Point.call(this, x, y); // (A)
        this.color = color;
    }
    ColorPoint.prototype = Object.create(Point.prototype); // (B)
    ···
    
    var cp = new ColorPoint(25, 8, 'green');

You want to inherit two things:

  • The instance properties of Point are inherited by calling it while setting this to the current instance of ColorPoint (line A). That leads to Point adding its instance properties to that instance.
  • The prototype properties in Point.prototype are inherited by making it the prototype of ColorPoint.prototype (line B).

The following diagram shows what objects exist after creating cp in line C.

[In order to keep things simple, I’ve omitted two things from the diagram that are not essential for understanding what is going on. First, Point is a sub-constructor of Object (its prototype is Object.prototype), as all constructors are, by default. Second, Point and ColorPoint are both instances of Function (they have the same prototype object, Function.prototype).]

Tip for understanding this diagram: [[Prototype]] is an inheritance relationship between objects, while prototype is a normal property whose value is an object. The property prototype is only special because the new operator makes its value the prototype of every instance it creates.

Creating cp is all about setting up the prototype chain shown in the right column. This prototype chain determines what properties cp has. The constructors contribute to the chain in two ways: First, they add own properties to cp. Second, the objects pointed to by their prototype properties become members of the chain (or rather, ColorPoint.prototype becomes the prototype object of cp).

Subclassing in ECMAScript 6

In ECMAScript 6, subclassing looks as follows.

    class Point {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
        ···
    }
    
    class ColorPoint extends Point {
        constructor(x, y, color) {
            super(x, y);
            this.color = color;
        }
        ···
    }
    
    let cp = new ColorPoint(25, 8, 'green');

The result is similar to what the ES5 code produced.

Even though the result is similar, ES6 gets there differently. This is an approximation of what goes on under the hood:

    // Allocate instance here
    function Point(x, y) {
        this = Object.create(new.target);
        this.x = x;
        this.y = y;
    }
    
    function ColorPoint(x, y, color) {
        this = Reflect.construct(Point, [x, y], new.target); // (A)
        this.color = color;
    }
    Object.setPrototypeOf(ColorPoint, Point);
    
    let cp = Reflect.construct(ColorPoint, [25, 8], ColorPoint); // (B)

Constructors now have an implicit parameter new.target that is originally filled in by the new operator (line B) and passed on by super (line A). Reflect.construct() [4] lets you do a constructor-call while specifying new.target via the last parameter.

Therefore, ES6 does two things quite differently.

First, the prototype of ColorPoint is Point. That means that ColorPoint inherits all of Point‘s properties.

Second, if the new operator constructor-calls the first in a chain of constructors (where derived constructors make super-constructor calls until execution reaches a base constructor) then ES6 and ES6 behave differently:

  • In ES5, the first in a chain of constructors allocates an instance and passes it on to its super-constructor. This continues until a base constructor is reached.
  • In ES6, the last in a chain of constructors allocates an instance. This happens as follows. this is uninitialized when entering a derived constructor. super() constructor-calls the super-constructor (and later initializes this with the result). This continues until execution reaches a base constructor, which allocates the instance, setting its prototype to new.target.

The advantage of the new approach is that it enables normal code to subclass built-in constructors (such as Error and Array).

This was a short explanation of ES6 subclassing. I’ll explain it more thoroughly later on.

Making super-calls in methods

The following ES5 code makes a super-call in line (A).

    function Point(x, y) {
        this.x = x;
        this.y = y;
    }
    Point.prototype.toString = function () {
        return '(' + this.x + ', ' + this.y + ')';
    };
    
    function ColorPoint(x, y, color) {
        Point.call(this, x, y);
        this.color = color;
    }
    ColorPoint.prototype = Object.create(Point.prototype);
    ColorPoint.prototype.toString = function () {
        return Point.prototype.toString.call(this) // (A)
               + ' in ' + this.color;
    };
    
    var cp = new ColorPoint(25, 8, 'green');
    console.log(cp.toString()); // (25, 8) in green

The same code looks as follows in ECMAScript 6.

    class Point {
        constructor(x, y) {
            this.x = x;
            this.y = y;
        }
        toString () { // (A)
            return '(' + this.x + ', ' + this.y + ')';
        }
    }
    
    class ColorPoint extends Point {
        constructor(x, y, color) {
            super(x, y);
            this.color = color;
        }
        toString () {
            return super.toString() // (B)
                   + ' in ' + this.color;
        }
    }
    
    let cp = new ColorPoint(25, 8, 'green');
    console.log(cp.toString()); // (25, 8) in green

To understand how super-calls work, let’s look at the object diagram of cp:

ColorPoint.prototype.toString makes a super-call (line B) to the method (starting in line A) that it has overridden. Let’s call the object, in which a method is stored, the home object of that method. For example, ColorPoint.prototype is the home object of ColorPoint.prototype.toString().

The super-call in line B involves three steps:

  1. Start your search in the prototype of the home object of the current method.
  2. Look for a method whose name is toString. That method may be found in the object where the search started or later in the prototype chain.
  3. Call that method with the current this. The reason for doing so is: the super-called method must be able to access the same instance properties (in our example, the properties of cp).

Let’s express these steps in three different, but equivalent, ways:

    // Variation 1
    var result = Point.prototype.toString.call(this) // steps 1,2,3
    
    // Variation 2
    var superObject = Point.prototype; // step 1
    var superMethod = superObject.toString; // step 2
    var result = superMethod.call(this) // step 3
    
    // Variation 3
    var homeObject = ColorPoint.prototype;
    var superObject = Object.getPrototypeOf(homeObject); // step 1
    var superMethod = superObject.toString; // step 2
    var result = superMethod.call(this) // step 3

Variation 3 is how ECMAScript 6 handles super-calls. This approach is supported by two internal bindings that the environments of functions have (environments provide storage space, so-called bindings, for the variables in a scope):

  • [[thisValue]]: You already know this internal binding, it also exists in ECMAScript 5 and stores the value of this.
  • [[HomeObject]]: Refers to the home object of the environment’s function. Filled in via an internal property [[HomeObject]] that all methods have that use super. Both the binding and the property are new in ECMAScript 6.

A method definition in a class literal that uses super is now special: Its value is still a function, but it has the internal property [[HomeObject]]. That property is set up by the method definition and can’t be changed in JavaScript. Therefore, you can’t meaningfully move such a method to a different object.

Using super to refer to a property is not allowed in function declarations, function expressions and generator functions. They are still allowed in method definitions inside object literals, but that may still change. Using super in an arrow function is the same as using it in the surrounding function or method.

The ES6 way of calling super-constructors

The following ES6 code makes a super-constructor call in line (A).

    class ColorPoint extends Point {
        constructor(x, y, color) {
            super(x, y); // (A)
            ···
        }
        ···
    }

This way of using super is only available in construct of derived classes (Sect. 14.5.1 in the spec). It works completely differently from the canonical way of subclassing in ES5. Before we examine how it works, let us first understand why you can’t subclass built-in constructors in ES5.

The problem with ES5 subclassing

In ECMAScript 5, most built-in constructors can’t be subclassed (several work-arounds exist).

To understand why, let’s use the canonical ES5 pattern to subclass Array. As we shall soon find out, this doesn’t work.

    function MyArray(len) {
        Array.call(this, len); // (A)
    }
    MyArray.prototype = Object.create(Array.prototype);

Unfortunately, if we instantiate MyArray, we find out that it doesn’t work properly: The instance property length does not change in reaction to us adding array elements:

    > var myArr = new MyArray(0);
    > myArr.length
    0
    > myArr[0] = 'foo';
    > myArr.length
    0

There are two obstracles that prevent myArr from being a proper array.

First obstacle: initialization. The this you hand to the constructor Array (in line A) is completely ignored. That means you can’t use Array to set up the instance that was created for MyArray.

    > var a = [];
    > var b = Array.call(a, 3);
    > a !== b  // a is ignored, b is a new object
    true
    > b.length // set up correctly
    3
    > a.length // unchanged
    0

Second obstacle: allocation. The instance objects created by Array are exotic (a term used by the ECMAScript specification for objects that have features that normal objects don’t have): Their property length tracks and influences the management of array elements. In general, exotic objects can be created from scratch, but you can’t convert an existing normal object into an exotic one. Unfortunately, that is what Array would have to do, when called in line A: It would have to turn the normal object created for MyArray into an exotic array object.

The solution: ES6 subclassing

In ECMAScript 6, subclassing Array looks as follows:

    class MyArray extends Array {
        constructor(len) {
            super(len);
        }
    }

This works (but it’s not something that ES6 transpilers can support, it depends on whether a JavaScript engine supports it natively):

    > let myArr = new MyArray(0);
    > myArr.length
    0
    > myArr[0] = 'foo';
    > myArr.length
    1

In ES6, super-constructor calls make use of two internal bindings of function environments (where the variables of function scopes are stored):

  • [[NewTarget]] is an implicit parameter of constructors (similar to the implicit parameter this of methods). It refers to the operand of the new operator that led to the current constructor call. In functions that weren’t constructor-called, its value is undefined.
  • [[FunctionObject]] refers to the function that is currently being executed.

So how does ES6 enable subclassing of built-in constructors? Classes that have an extends clause produce derived constructors and those handle object allocation differently from normal constructors (base constructors):

  • When a derived constructor is entered, its this is uninitialized. That means that getting or setting this leads to a ReferenceError.
  • If the constructor calls super() then that triggers a constructor call to the super-constructor, the result of that call is used to initialize this.

The new operator calls the first derived constructor and sets [[NewTarget]] to that constructor. Afterwards, super() passes [[NewTarget]] on to the next constructor. There may be a whole chain of derived constructors. Eventually, the constructor calls reach a base constructor:

  • Before execution enters a base constructor, a fresh object is created whose prototype is [[NewTarget]].prototype. this is set to that object when entering the constructor.
  • Built-in constructors override the default mechanism and return an exotic object. But the prototype of that object is still [[NewTarget]].prototype.

We can now see how the new approach of subclassing circumvents the obstacles:

  • Allocation happens in the base constructor, which means that Array can allocate an exotic object. While most of the new approach is due to how derived constructors behave, this step requires that a base constructor is aware of [[NewTarget]] and makes [[NewTarget]].prototype the protoype of the allocated instance.
  • Initialization also happens in the base constructor, a derived constructor receives an initialized object and works with that one instead of passing its own instance to the super-constructor and requiring it to set it up.
new.target

The internal variable [[NewTarget]] is available as new.target in ES6. Therefore, what a derived constructor such as MyArray does, looks roughly as follows.

    function MyArray(len) {
        this = Reflect.construct(Array, [len], new.target);
        // If this constructor is invoked via `new TargetConstr(···)
        // then the prototype of `this` is `TargetConstr.prototype`.
    }

Reflect.construct() [4] performs a constructor call. Its last parameter is the new-target. That is, MyArray passes its new-target on and can be subclassed, too.

Two facts about new.target:

  • new.target is available in all functions. It is undefined during a function call (as opposed to a constructor call). Therefore, new.target lets you determine whether a function was constructor-called or not.
  • new.target in arrow functions is the [[NewTarget]] of the surrounding (non-arrow) function or method.
Safety checks
  • this originally being uninitialized in derived constructors means that an error is thrown if they access this in any way before they have called super().
  • Once this is initialized, calling super() produces a ReferenceError. This protects you against calling super() twice.
  • If a constructor returns implicitly (without a return statement), the result is this. If this is uninitialized, a ReferenceError is thrown. This protects you against forgetting to call super().
  • If a constructor explicitly returns a non-object (including undefined and null), the result is this (this behavior is required to remain compatible with ES5 and earlier). If this is uninitialized, a TypeError is thrown.
  • If a constructor explicitly returns a non-object, it is used as its result. Then it doesn’t matter whether this is initialized or not.
The extends clause

Let’s examine how the extends clause influences how a class is set up (Sect. 14.5.14 of the spec).

The value of an extends clause must be “constructible” (invocable via new). null is allowed, though.

    class C {
    }
  • Constructor kind: base
  • Prototype of C: Function.prototype (like a normal function)
  • Prototype of C.prototype: Object.prototype (which is also the prototype of objects created via object literals)
    class C extends B {
    }
  • Constructor kind: derived
  • Prototype of C: B
  • Prototype of C.prototype: B.prototype
    class C extends Object {
    }
  • Constructor kind: derived
  • Prototype of C: Object
  • Prototype of C.prototype: Object.prototype

Note the following subtle difference with the first case: If there is no extends clause, the class is a base class and allocates instances. If a class extends Object, it is a derived class and Object allocates the instances. The resulting instances (including their prototype chains) are the same, but you get there differently.

    class C extends null {
    }
  • Constructor kind: derived
  • Prototype of C: Function.prototype
  • Prototype of C.prototype: null

Such a class is not very useful: new-calling it leads to an error, because the default constructor makes a super-constructor call and Function.prototype (the super-constructor) can’t be constructor-called. The only way to make the error go away is by adding a constructor that returns an object.

Constructor calls explained via JavaScript code

The JavaScript code in this section is a much simplified version of how the specification describes constructor calls and super-constructor calls. It may be interesting to you if you prefer code to explanations in human language. Before we can delve into the actual functionality, we need to understand a few other mechanisms.

Internal variables and properties

The specification writes internal variables and properties in double brackets ([[Foo]]). In the code, I use double underscores, instead (__Foo__).

Internal variables used in the code:

  • [[NewTarget]]: The operand of the new operator that triggered the current constructor call (passed on if [[Construct]] is called recursively via super()).
  • [[thisValue]]: Stores the value of this.
  • [[FunctionObject]]: Refers to the function that is currently executed.

Internal properties used in the code:

  • [[Construct]]: All constructor functions (including those created by classes) have this own (non-inherited) method. It implements constructor calls and is invoked by new.
  • [[ConstructorKind]]: A property of constructor functions whose value is either 'base' or 'derived'.

Environments

Environments provide storage space for variables, there is one environment per scope. Environments are managed as a stack. The environment on top of that stack is considered active. The following code is a sketch of how environments are handled.

    /**
     * Function environments are special, they have a few more
     * internal variables than other environments.
     * (`Environment` is not shown here)
     */
    class FunctionEnvironment extends Environment {
        constructor(Func) {
            // [[FunctionObject]] is a function-specific
            // internal variable
            this.__FunctionObject__ = Func;
        }    
    }
    
    /**
     * Push an environment onto the stack
     */
    function PushEnvironment(env) { ··· }
    
    /**
     * Pop the topmost environment from the stack
     */
    function PopEnvironment() { ··· }
    
    /**
     * Find topmost function environment on stack
     */
    function GetThisEnvironment() { ··· }

Constructor calls

Let’s start with the default way (ES6 spec Sect. 9.2.3) in which constructor calls are handled for functions:

    /**
     * All constructible functions have this own method,
     * it is called by the `new` operator
     */
    AnyFunction.__Construct__ = function (args, newTarget) {
        let Constr = this;
        let kind = Constr.__ConstructorKind__;
    
        let env = new FunctionEnvironment(Constr);
        env.__NewTarget__ = newTarget;
        if (kind === 'base') {
            env.__thisValue__ = Object.create(newTarget.prototype);
        } else {
            // While `this` is uninitialized, getting or setting it
            // throws a `ReferenceError`
            env.__thisValue__ = uninitialized;
        }
    
        PushEnvironment(env);
        let result = Constr(...args);
        PopEnvironment();
    
        // Let's pretend there is a way to tell whether `result`
        // was explicitly returned or not
        if (WasExplicitlyReturned(result)) {
            if (isObject(result)) {
                return result;
            }
            // Explicit return of a primitive
            if (kind === 'base') {
                // Base constructors must be backwards compatible
                return env.__thisValue__; // always initialized!
            }
            throw new TypeError();
        }
        // Implicit return
        if (env.__thisValue__ === uninitialized) {
            throw new ReferenceError();
        }
        return env.__thisValue__;
    }

Super-constructor calls

Super-constructor calls are handled as follows (ES6 spec Sect. 12.3.5.1).

    /**
     * Handle super-constructor calls
     */
    function super(...args) {
        let env = GetThisEnvironment();
        let newTarget = env.__NewTarget__;
        let activeFunc = env.__FunctionObject__;
        let superConstructor = Object.getPrototypeOf(activeFunc);
    
        env.__thisValue__ = superConstructor
                            .__Construct__(args, newTarget);
    }

The species pattern

One more mechanism of built-in constructors has been made extensible in ECMAScript 6: If a method such as Array.prototype.map() returns a fresh instance, what constructor should it use to create that instance? The default is to use the same constructor that created this, but some subclasses may want it to remain a direct instance of Array. ES6 lets subclasses override the default, via the so-called species pattern:

  • When creating a new instance of Array, methods such as map() use the constructor stored in this.constructor[Symbol.species].
  • If a sub-constructor of Array does nothing, it inherits Array[Symbol.species]. That property is a getter that returns this.

You can override the default, via a static getter (line A):

    class MyArray1 extends Array {
    }
    let result1 = new MyArray1().map(x => x);
    console.log(result1 instanceof MyArray1); // true
    
    class MyArray2 extends Array {
        static get [Symbol.species]() { // (A)
            return Array;
        }
    }
    let result2 = new MyArray2().map(x => x);
    console.log(result2 instanceof MyArray2); // false

An alternative is to use Object.defineProperty() (you can’t use assignment, as that would trigger a setter, which doesn’t exist):

    Object.defineProperty(
        MyArray2, Symbol.species, {
            value: Array
        });

The following getters all return this, which means that methods such as Array.prototype.map() use the constructor that created the current instance for their results.

  • Array.get [Symbol.species]()
  • ArrayBuffer.get [Symbol.species]()
  • Map.get [Symbol.species]()
  • Promise.get [Symbol.species]()
  • RegExp.get [Symbol.species]()
  • Set.get [Symbol.species]()
  • %TypedArray%.get [Symbol.species]()

Conclusion

The specialization of functions

There is an interesting trend in ECMAScript 6: Previously, a single kind of function took on three roles: real function, method and constructor. In ES6, there is specialization:

  • Arrow functions are specialized for non-method callbacks, where them picking up the this of their surrounding method or constructor is an advantage. Without this, they don’t make much sense as methods and they throw an exception when invoked via new.
  • Method definitions enable the use of super, by setting up the property [[HomeObject]]. The functions they produce can’t be constructor-called.
  • Class definitions are the only way to create derived constructors (enabling ES6-style subclassing that works for built-in constructors). Class definitions produce functions that can only be constructor-called.

The future of classes

The design maxim for classes was “maximally minimal”. Several advanced features were discussed, but ultimately discarded in order to get a design that would be unanimously accepted by TC39.

Upcoming versions of ECMAScript can now extend this minimal design – classes will provide a foundation for features such as traits (or mixins), value objects (where different objects are equal if they have the same content) and const classes (that produce immutable instances).

Does JavaScript need classes?

Classes are controversial within the JavaScript community. On one hand, people coming from class-based languages are happy that they don’t have to deal with JavaScript’s unorthodox inheritance mechanisms, anymore. On the other hand, there are many JavaScript programmers who argue that what’s complicated about JavaScript is not prototypal inheritance, but constructors [5].

ES6 classes provide a few clear benefits:

  • They are backwards compatible with much of the current code.
  • Compared to constructors and constructor inheritance, classes make it easier for beginners to get started.
  • Subclassing is supported within the language.
  • Built-in constructors are subclassable.
  • No library for inheritance is needed, anymore; code will become more portable between frameworks.
  • They provide a foundation for advanced features in the future (mixins and more).
  • They help tools that statically analyze code (IDEs, type checkers, style checkers, etc.).

I have made my peace with classes and am glad that they are in ES6. I would have preferred them to be prototypal (based on constructor objects [5], not constructor functions), but I also understand that backwards compatibility is important.

Further reading

Acknowledgement: #1 was an important source of this blog post.

  1. Instantiation Reform: One last time”, slides by Allen Wirfs-Brock.
  2. Exploring ES6: Upgrade to the next version of JavaScript”, book by Axel
  3. Iterators and generators in ECMAScript 6
  4. Meta programming with ECMAScript 6 proxies
  5. Prototypes as classes – an introduction to JavaScript inheritance

Source:: 2ality

Quick Tip: Add Keyboard Shortcuts To Your Web App

By Nick Anastasov

quick-tip-add-keyboard-shortcuts-to-your-web-app

We power users love our keyboard shortcuts. We use them everywhere – in our code editor, in Photoshop, in gmail. And we hate it when we hit Ctrl+S in a web app, only to see our browser offering to download it. Adding shortcuts to your application is not hard at all. In this quick tip, we will show you how to do it, by using Mousetrap.js.

1. Single Keys

Single keys are easy. You can do it with a simple event listener for keypress on the document object. But with Mousetrap, it is even better.

After hitting the run button, focus the editor by clicking on it. Otherwise, it won’t register your key presses and the demo won’t work.

(Play with our code editor on Tutorialzine.com)

2. Alternative Symbols

Mousetrap shines when listening for more complex key combinations like capital letters and special symbols.

(Play with our code editor on Tutorialzine.com)

3. Key combinations

Combinations that involve the Control key are equally easy (see the next example for how to listen for both Control and the OS X Command key).

(Play with our code editor on Tutorialzine.com)

4. Multiple Combinations

Passing an array instead of a string lets you listen for multiple key combinations at once. This is useful when you have to listen for combinations which involve the Control (for Windows and Linux) and Command (for Mac) keys.

(Play with our code editor on Tutorialzine.com)

5. Sequences

This type of shortcuts are very powerful, and are used in apps like gmail. Works with array keys as well!

(Play with our code editor on Tutorialzine.com)

Conclusion

This was our quick tip on keyboard shortcuts. If you’ve used keyboard hotkeys before, or are brave enough to experiment with them in your next project, do share the results with us in the comments below.

Source:: Tutorialzine.com

Variables and scoping in ECMAScript 6

By Axel Rauschmayer

This blog post examines how variables and scoping are handled in ECMAScript 6 [1].

Block scoping via let and const

Both let and const create variables that are block-scoped – they only exist within the innermost block that surrounds them. The following code demonstrates that the let-declared variable tmp only exists inside the then-block of the if statement:

    function func() {
        if (true) {
            let tmp = 123;
        }
        console.log(tmp); // ReferenceError: tmp is not defined
    }

In contrast, var-declared variables are function-scoped:

    function func() {
        if (true) {
            var tmp = 123;
        }
        console.log(tmp); // 123
    }

Block scoping means that you can shadow variables within a function:

    function func() {
      let foo = 5;
      if (···) {
         let foo = 10; // shadows outer `foo`
         console.log(foo); // 10
      }
      console.log(foo); // 5
    }

const creates immutable variables

Variables created by let are mutable:

    let foo = 'abc';
    foo = 'def';
    console.log(foo); // def

Variables created by const, constants, are immutable:

    const foo = 'abc';
    foo = 'def'; // TypeError

Note that const does not affect whether the value of a constant itself is mutable or not: If a constant refers to an object, it will always refer to that object, but the object itself can still be changed (if it is mutable).

    const obj = {};
    obj.prop = 123;
    console.log(obj.prop); // 123

If you wanted obj to truly be a constant, you’d have to freeze its value:

    const obj = Object.freeze({});
    obj.prop = 123; // TypeError

const in loop bodies

Once a const variable has been created, it can’t be changed. But that doesn’t mean that you can’t re-enter its scope and start fresh, with a new value. For example, via a loop:

    function logArgs(...args) {
        for (let [index, elem] of args.entries()) {
            const message = index + '. ' + elem;
            console.log(message);
        }
    }
    logArgs('Hello', 'everyone');
    
    // Output:
    // 0. Hello
    // 1. everyone

When should I use let, when const?

If you want to mutate variables that hold primitive values, you can’t use const:

    const foo = 1;
    foo++; // TypeError

However, for objects, you can use const, because only the binding is immutable, not the value itself:

    const bar = [];
    bar.push('abc'); // OK

I’m still mulling over what the best style is, but I currently use let in situations like the previous example, because bar itself is mutable. I do use const to indicate that both binding and value are immutable:

    const EMPTY_ARRAY = Object.freeze([]);

The temporal dead zone

A variable declared by let or const has a so-called temporal dead zone (TDZ): When entering its scope, it can’t be accessed (got or set) until execution reaches the declaration.

Let’s first examine the life cycle of var variables, which don’t have temporal dead zones:

  • When the scope (its surrounding function) of a var variable is entered, storage space (a so-called binding) is created for it. The variable is immediately initialized, by setting it to undefined.

  • When the execution within the scope reaches the declaration, the variable is set to the value specified by the initializer (an assignment) – if there is one. If there isn’t, the value value of the variable remains undefined.

Variables declared via let have temporal dead zones, which means that their life cycles look like this:

  • When the scope (its surrounding block) of a let variable is entered, storage space (a so-called binding) is created for it. The variable remains uninitialized.

  • Getting or setting an uninitialized causes a ReferenceError.

  • When the execution within the scope reaches the declaration, the variable is set to the value specified by the initializer (an assignment) – if there is one. If there isn’t, the value of the variable is set to undefined.

const variables work similarly to let variables, but they must have an initializer (i.e., be set to a value immediately) and can’t be changed.

Within a TDZ, an exception is thrown if a variable is got or set:

    if (true) { // enter new scope, TDZ starts
    
        // Uninitialized binding for `tmp` is created
        tmp = 'abc'; // ReferenceError
        console.log(tmp); // ReferenceError
    
        let tmp; // TDZ ends, `tmp` is initialized with `undefined`
        console.log(tmp); // undefined
    
        tmp = 123;
        console.log(tmp); // 123
    }

The following example demonstrates that the dead zone is really temporal (based on time) and not spatial (based on location):

    if (true) { // enter new scope, TDZ starts
        const func = function () {
            console.log(myVar); // OK!
        };
    
        // Here we are within the TDZ and
        // accessing `myVar` causes a ReferenceError
    
        let myVar = 3; // TDZ ends
        func(); // called outside TDZ
    }

typeof and the temporal dead zone

A variable being unaccessible in the temporal dead zone means that you can’t even apply typeof to it:

    if (true) {
        console.log(typeof tmp); // ReferenceError
        let tmp;
    }

I don’t expect this to be a problem in practice, because you can’t conditionally add let-declared variables to a scope. In contrast, you can do so for var-declared variables; assigning to a property of window creates a global var variable:

    if (typeof myVarVariable === `undefined`) {
        // `myVarVariable` does not exist => create it
        window.myVarVariable = 'abc';
    }

let in loop heads

In loops, you get a fresh binding for each iteration if you let-declare a variable. The loops that allow you to do so are: for, for-in and for-of.

This looks as follows:

    let arr = [];
    for (let i=0; i < 3; i++) {
        arr.push(() => i);
    }
    console.log(arr.map(x => x())); // [0,1,2]

In contrast, a var declaration leads to a single binding for the whole loop (a const declaration works the same):

    let arr = [];
    for (var i=0; i < 3; i++) {
        arr.push(() => i);
    }
    console.log(arr.map(x => x())); // [3,3,3]

Getting a fresh binding for each iteration may seem strange at first, but it is very useful whenever you use loops to create functions (e.g. callbacks for event handling) that refer to loop variables.

Parameters

Parameters versus local variables

If you let-declare a variable that has the same name as a parameter, you get a static (load-time) error:

    function func(arg) {
        let arg; // static error: duplicate declaration of `arg`
    }

Doing the same inside a block shadows the parameter:

    function func(arg) {
        {
            let arg; // shadows parameter `arg`
        }
    }

In contrast, var-declaring a variable that has the same name as a parameter does nothing, just like re-declaring a var variable within the same scope does nothing.

    function func(arg) {
        var arg; // does nothing
    }
    function func(arg) {
        {
            // We are still in same `var` scope as `arg`
            var arg; // does nothing
        }
    }

Parameter default values and the temporal dead zone

If parameters have default values [2], they are treated like a sequence of let statements and are subject to temporal dead zones:

    // OK: `y` accesses `x` after it has been declared
    function foo(x=1, y=x) {
        return [x, y];
    }
    foo(); // [1,1]
    
    // Exception: `x` tries to access `y` within TDZ
    function bar(x=y, y=2) {
        return [x, y];
    }
    bar(); // ReferenceError

Parameter default values don’t see the scope of the body

The scope of parameter default values is separate from the scope of the body (the former surrounds the latter). That means that methods or functions defined “inside” parameter default values don’t see the local variables of the body:

    let foo = 'outer';
    function bar(func = x => foo) {
        let foo = 'inner';
        console.log(func()); // outer
    }
    bar();

The global object

JavaScript’s global object (window in web browsers, global in Node.js) is more a bug than a feature, especially with regard to performance. That’s why it’s not surprising that ES6 introduces a distinction:

  • All properties of the global object are global variables. In global scope, the following declarations create such properties:
    • var declarations
    • Function declarations
  • But there are now also global variables that are not properties of the global object. In global scope, the following declarations create such variables:
    • let declarations
    • const declarations
    • Class declarations

Function declarations and class declarations

Function declarations…

  • are block-scoped, like let.
  • create properties on the global object (while in global scope), like var.
  • are hoisted: independently of where a function declaration is mentioned in its scope, it is always created at the beginning of the scope.

The following code demonstrates the hoisting of function declarations:

    { // Enter a new scope
    
        console.log(foo()); // OK, due to hoisting
        function foo() {
            return 'hello';
        }
    }

Class declarations…

  • are block-scoped.
  • don’t create properties on the global object.
  • are not hoisted.

Classes not being hoisted may be surprising, because, under the hood, they create functions. The rationale for this behavior is that the values of their extends clauses are defined via expressions and those expressions have to be executed at the appropriate times.

    { // Enter a new scope
    
        const identity = x => x;
    
        // Here we are in the temporal dead zone of `MyClass`
        let inst = new MyClass(); // ReferenceError
    
        // Note the expression in the `extends` clause
        class MyClass extends identity(Object) {
        }
    }

Further reading

  1. Using ECMAScript 6 today [an early draft of my book on ECMAScript 6]
  2. Destructuring and parameter handling in ECMAScript 6

Source:: 2ality

Making a Single Page App Without a Framework

By Danny Markov

making-a-simple-page-app-without-a-framework

The idea behind single page applications (SPA) is to create a smooth browsing experience like the one found in native desktop apps. All of the necessary code for the page is loaded only once and its content gets changed dynamically through JavaScript. If everything is done right the page shouldn’t ever reload, unless the user refreshes it manually.

There are many frameworks for single page applications out there. First we had Backbone, then Angular, now React. It takes a lot of work to constantly learn and re-learn things (not to mention having to support old code you’ve written in a long forgotten framework). In some situations, like when your app idea isn’t too complex, it is actually not that hard to create a single page app without using any external frameworks. Here is how to do it.

Note: To run this example after downloading it, you need a locally running webserver like Apache. Our demo uses AJAX so it will not work if you simply double-click index.html for security reasons.

The Idea

We will not be using a framework, but we will be using two libraries – jQuery for DOM manipulation and event handling, and Handlebars for templates. You can easily omit these if you wish to be even more minimal, but we will use them for the productivity gains they provide. They will be here long after the hip client-side framework of the day is forgotten.

The app that we will be building fetches product data from a JSON file, and displays it by rendering a grid of products with Handlebars. After the initial load, our app will stay on the same URL and listen for changes to the hash part with the hashchange event. To navigate around the app, we will simply change the hash. This has the added benefit that browser history will just work without extra effort on our part.

The Setup

Our project’s folder

As you can see there isn’t much in our project folder. We have the regular web app setup – HTML, JavaScript and CSS files, accompanied by a products.json containing data about the products in our shop and a folder with images of the products.

The Products JSON

The .json file is used to store data about each product for our SPA. This file can easily be replaced by a server-side script to fetch data from a real database.

products.json

[
  {
    "id": 1,
    "name": "Sony Xperia Z3",
    "price": 899,
    "specs": {
      "manufacturer": "Sony",
      "storage": 16,
      "os": "Android",
      "camera": 15
    },
    "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam tristique ipsum in efficitur pharetra. Maecenas luctus ante in neque maximus, sed viverra sem posuere. Vestibulum lectus nisi, laoreet vel suscipit nec, feugiat at odio. Etiam eget tellus arcu.",
    "rating": 4,
    "image": {
      "small": "/images/sony-xperia-z3.jpg",
      "large": "/images/sony-xperia-z3-large.jpg"
    }
  },
  {
    "id": 2,
    "name": "Iphone 6",
    "price": 899,
    "specs": {
      "manufacturer": "Apple",
      "storage": 16,
      "os": "iOS",
      "camera": 8
    },
    "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam tristique ipsum in efficitur pharetra. Maecenas luctus ante in neque maximus, sed viverra sem posuere. Vestibulum lectus nisi, laoreet vel suscipit nec, feugiat at odio. Etiam eget tellus arcu.",
    "rating": 4,
    "image": {
      "small": "/images/iphone6.jpg",
      "large": "/images/iphone6-large.jpg"
    }
  }
]

The HTML

In our html file we have several divs sharing the same class “page”. Those are the different pages (or as they are called in SPA – states) our app can show. However, on page load all of these are hidden via CSS and need the JavaScript to show them. The idea is that only one page can be visible at a time and our script is the one to decide which one it is.

index.html

<div class="main-content">

	<div class="all-products page">

		<h3>Our products</h3>

		<div class="filters">
			<form>
				Checkboxes here
			</form>
		</div>

    <ul class="products-list">
      <script id="products-template" type="x-handlebars-template">​
        {{#each this}}
          <li data-index="{{id}}">
            <a href="#" class="product-photo"><img src="{{image.small}}" height="130" alt="{{name}}"/></a>
            <h2><a href="#"> {{name}} </a></h2>
            <ul class="product-description">
              <li><span>Manufacturer: </span>{{specs.manufacturer}}</li>
              <li><span>Storage: </span>{{specs.storage}} GB</li>
              <li><span>OS: </span>{{specs.os}}</li>
              <li><span>Camera: </span>{{specs.camera}} Mpx</li>
            </ul>
            <button>Buy Now!</button>
            <p class="product-price">{{price}}$</p>
            <div class="highlight"></div>
          </li>
        {{/each}}
      </script>

    </ul>

	</div>


	<div class="single-product page">

		<div class="overlay"></div>

		<div class="preview-large">
			<h3>Single product view</h3>
			<img src=""/>
			<p></p>

			<span class="close">&times;</span>
		</div>

	</div>

	<div class="error page">
		<h3>Sorry, something went wrong :(</h3>
	</div>

</div>

We have three pages: all-products (the product listing), single-product (the individual product page) and error.

The all-products page consists of a title, a form containing checkboxes for filtering and a

    tag with the class “products-list”. This list is generated with handlebars using the data stored in products.json, creating a

  • for each entry in the json. Here is the result:

    The Products

    The Products

    Single-product is used to show information about only one product. It is empty and hidden on page load. When the appropriate hash address is reached, it is populated with product data and shown.

    The error page consist of only an error message to let you know when you’ve reached a faulty address.

    The JavaScript Code

    First, lets make a quick preview of the functions and what they do.

    script.js

    $(function () {
    
    	checkboxes.click(function () {
    		// The checkboxes in our app serve the purpose of filters.
    		// Here on every click we add or remove filtering criteria from a filters object.
    
    		// Then we call this function which writes the filtering criteria in the url hash.
    		createQueryHash(filters);
    	});
    
    
    
    	$.getJSON( "products.json", function( data ) {
    		// Get data about our products from products.json.
    
    		// Call a function that will turn that data into HTML.
    		generateAllProductsHTML(data);
    
    		// Manually trigger a hashchange to start the app.
    		$(window).trigger('hashchange');
    	});
    
    
    	$(window).on('hashchange', function(){
    		// On every hash change the render function is called with the new hash.
    		// This is how the navigation of our app happens.
    		render(window.location.hash);
    	});
    
    
    	function render(url) {
    		// This function decides what type of page to show 
    		// depending on the current url hash value.
    	}
    
    
    	function generateAllProductsHTML(data){
    		// Uses Handlebars to create a list of products using the provided data.
    		// This function is called only once on page load.
    	}
    
    
    	function renderProductsPage(data){
    		// Hides and shows products in the All Products Page depending on the data it recieves.
    	}
    
    
    	function renderSingleProductPage(index, data){
    		// Shows the Single Product Page with appropriate data.
    	}
    
    	function renderFilterResults(filters, products){
    		// Crates an object with filtered products and passes it to renderProductsPage.
    		renderProductsPage(results);
    	}
    
    	function renderErrorPage(){
    		// Shows the error page.
    	}
    
    	
    	function createQueryHash(filters){
    		// Get the filters object, turn it into a string and write it into the hash.
    	}
    
    });
    

    Remember that the concept of SPA is to not have any loads going on while the app is running. That’s why after the initial page load we want to stay on the same page, where everything we need has already been fetched by the server.

    However, we still want to be able to go somewhere in the app and, for example, copy the url and send it to a friend. If we never change the app’s address they will just get the app the way it looks in the beginning, not what you wanted to share with them. To solve this problem we write information about the state of the app in the url as #hash. Hashes don’t cause the page to reload and are easily accessible and manipulated.

    On every hashchange we call this:

    function render(url) {
    
    		// Get the keyword from the url.
    		var temp = url.split('/')[0];
    
    		// Hide whatever page is currently shown.
    		$('.main-content .page').removeClass('visible');
    
    
    		var map = {
    
    			// The Homepage.
    			'': function() {
    
    				// Clear the filters object, uncheck all checkboxes, show all the products
    				filters = {};
    				checkboxes.prop('checked',false);
    
    				renderProductsPage(products);
    			},
    
    			// Single Products page.
    			'#product': function() {
    
    				// Get the index of which product we want to show and call the appropriate function.
    				var index = url.split('#product/')[1].trim();
    
    				renderSingleProductPage(index, products);
    			},
    
    			// Page with filtered products
    			'#filter': function() {
    
    				// Grab the string after the '#filter/' keyword. Call the filtering function.
    				url = url.split('#filter/')[1].trim();
    
    				// Try and parse the filters object from the query string.
    				try {
    					filters = JSON.parse(url);
    				}
    				// If it isn't a valid json, go back to homepage ( the rest of the code won't be executed ).
    				catch(err) {
    					window.location.hash = '#';
    				}
    
    				renderFilterResults(filters, products);
    			}
    
    		};
    
    		// Execute the needed function depending on the url keyword (stored in temp).
    		if(map[temp]){
    			map[temp]();
    		}
    		// If the keyword isn't listed in the above - render the error page.
    		else {
    			renderErrorPage();
    		}
    
    	}
    

    This function takes into consideration the beginning string of our hash, decides what page needs to be shown and calls the according functions.

    For example if the hash is ‘#filter/{“storage”:[“16”],”camera”:[“5”]}’, our codeword is ‘#filter’. Now the render function knows we want to see a page with the filtered products list and will navigate us to it. The rest of the hash will be parsed into an object and a page with the filtered products will be shown, changing the state of the app.

    This is called only once on start up and turns our JSON into actual HTML5 content via handlebars.

    function generateAllProductsHTML(data){
    
        var list = $('.all-products .products-list');
    
        var theTemplateScript = $("#products-template").html();
        //Compile the template​
        var theTemplate = Handlebars.compile (theTemplateScript);
        list.append (theTemplate(data));
    
    
        // Each products has a data-index attribute.
        // On click change the url hash to open up a preview for this product only.
        // Remember: every hashchange triggers the render function.
        list.find('li').on('click', function (e) {
          e.preventDefault();
    
          var productIndex = $(this).data('index');
    
          window.location.hash = 'product/' + productIndex;
        })
      }
    

    This function receives an object containing only those products we want to show and displays them.

    function renderProductsPage(data){
    
        var page = $('.all-products'),
          allProducts = $('.all-products .products-list > li');
    
        // Hide all the products in the products list.
        allProducts.addClass('hidden');
    
        // Iterate over all of the products.
        // If their ID is somewhere in the data object remove the hidden class to reveal them.
        allProducts.each(function () {
    
          var that = $(this);
    
          data.forEach(function (item) {
            if(that.data('index') == item.id){
              that.removeClass('hidden');
            }
          });
        });
    
        // Show the page itself.
        // (the render function hides all pages so we need to show the one we want).
        page.addClass('visible');
    
      }
    

    Shows the single product preview page:

    function renderSingleProductPage(index, data){
    
        var page = $('.single-product'),
          container = $('.preview-large');
    
        // Find the wanted product by iterating the data object and searching for the chosen index.
        if(data.length){
          data.forEach(function (item) {
            if(item.id == index){
              // Populate '.preview-large' with the chosen product's data.
              container.find('h3').text(item.name);
              container.find('img').attr('src', item.image.large);
              container.find('p').text(item.description);
            }
          });
        }
    
        // Show the page.
        page.addClass('visible');
    
      }
    

    Takes all the products, filters them based on our query and returns an object with the results.

    function renderFilterResults(filters, products){
    
          // This array contains all the possible filter criteria.
        var criteria = ['manufacturer','storage','os','camera'],
          results = [],
          isFiltered = false;
    
        // Uncheck all the checkboxes.
        // We will be checking them again one by one.
        checkboxes.prop('checked', false);
    
    
        criteria.forEach(function (c) {
    
          // Check if each of the possible filter criteria is actually in the filters object.
          if(filters[c] && filters[c].length){
    
    
            // After we've filtered the products once, we want to keep filtering them.
            // That's why we make the object we search in (products) to equal the one with the results.
            // Then the results array is cleared, so it can be filled with the newly filtered data.
            if(isFiltered){
              products = results;
              results = [];
            }
    
    
            // In these nested 'for loops' we will iterate over the filters and the products
            // and check if they contain the same values (the ones we are filtering by).
    
            // Iterate over the entries inside filters.criteria (remember each criteria contains an array).
            filters[c].forEach(function (filter) {
    
              // Iterate over the products.
              products.forEach(function (item){
    
                // If the product has the same specification value as the one in the filter
                // push it inside the results array and mark the isFiltered flag true.
    
                if(typeof item.specs[c] == 'number'){
                  if(item.specs[c] == filter){
                    results.push(item);
                    isFiltered = true;
                  }
                }
    
                if(typeof item.specs[c] == 'string'){
                  if(item.specs[c].toLowerCase().indexOf(filter) != -1){
                    results.push(item);
                    isFiltered = true;
                  }
                }
    
              });
    
              // Here we can make the checkboxes representing the filters true,
              // keeping the app up to date.
              if(c && filter){
                $('input[name='+c+'][value='+filter+']').prop('checked',true);
              }
            });
          }
    
        });
    
        // Call the renderProductsPage.
        // As it's argument give the object with filtered products.
        renderProductsPage(results);
      }
    

    Shows the error state:

    function renderErrorPage(){
        var page = $('.error');
        page.addClass('visible');
      }
    

    Stringifies the filters object and writes it into the hash.

    function createQueryHash(filters){
    
        // Here we check if filters isn't empty.
        if(!$.isEmptyObject(filters)){
          // Stringify the object via JSON.stringify and write it after the '#filter' keyword.
          window.location.hash = '#filter/' + JSON.stringify(filters);
        }
        else{
          // If it's empty change the hash to '#' (the homepage).
          window.location.hash = '#';
        }
    
      }
    

    Conclusion

    Single page applications are perfect when you want give your project a more dynamic and fluid feel, and with the help of some clever design choices you can offer your visitors a polished, pleasant experience.

    Source:: Tutorialzine.com