ES Harmony

TC39, the standards body charged with defining the syntax and semantics of ECMAScript , has been keeping a close eye on the evolution of JavaScript usage for large-scale development over the past few years. One key area they’ve been reviewing is the possible need to support more advanced modules that cater for the needs of the modern JavaScript developer.

For this reason, there are currently proposals for a number of exciting additions to the language, including flexible modules that can work on both the client and server, a module loader, and more. In this section, we’ll explore code samples using the syntax proposed for modules in ES.next so we can get a taste of what’s to come.

Note

Although Harmony is still in the proposal phases, we can already try out (partial) features of ES.next that address native support for writing modular JavaScript, thanks to Google’s Traceur compiler. To get up and running with Traceur in under a minute, read this getting started guide. There’s also a JSConf presentation that’s worth looking at, if you’re interested in learning more about the project.

Modules with Imports and Exports

Having read through the sections on AMD and CommonJS modules, you may be familiar with the concept of module dependencies (imports) and module exports (or, the public API/variables we allow other modules to consume). In ES.next, these concepts have been proposed in a slightly more succinct manner with dependencies being specified using an import keyword. export isn’t greatly different to what we might expect, and many developers will look at the code samples lower down and instantly grab them.

  • import declarations bind a modules, exports as local variables and may be renamed to avoid name collisions/conflicts.

  • export declarations declare that a local binding of a module is externally visible, such that other modules may read the exports but can’t modify them. Interestingly, modules may export child modules but can’t export modules that have been defined elsewhere. We can also rename exports so their external names differ from their local names.

module staff{
    // specify (public) exports that can be consumed by
    // other modules
    export var baker = {
        bake: function( item ){
            console.log( "Woo! I just baked " + item );
        }
    }   
}

module skills{
    export var specialty = "baking";
    export var experience = "5 years";
}

module cakeFactory{

    // specify dependencies
    import baker from staff;

    // import everything with wildcards
    import * from skills;

    export var oven = {
        makeCupcake: function( toppings ){
            baker.bake( "cupcake", toppings );
        },
        makeMuffin: function( mSize ){
            baker.bake( "muffin", size );
        }
    }
}

Modules Loaded from Remote Sources

The module proposals also cater for modules which are remotely based (e.g. a third-party libraries) making it simplistic to load modules in from external locations. Here’s an example of pulling in the module we defined above and utilizing it:

module cakeFactory from "http://addyosmani.com/factory/cakes.js";
cakeFactory.oven.makeCupcake( "sprinkles" );
cakeFactory.oven.makeMuffin( "large" );

Module Loader API

The module loader proposed describes a dynamic API for loading modules in highly controlled contexts. Signatures supported on the loader include load(url, moduleInstance, error) for loading modules, createModule(object, globalModuleReferences), and others.

Here’s another example for dynamically loading in the module we initially defined. Note that unlike the last example where we pulled in a module from a remote source, the module loader API is better suited to dynamic contexts.

Loader.load( "http://addyosmani.com/factory/cakes.js" ,
    function( cakeFactory ){
        cakeFactory.oven.makeCupcake( "chocolate" );
    });

CommonJS-like Modules for the Server

For developers who are more interested in server environments, the module system proposed for ES.next isn’t just constrained to looking at modules in the browser. Here, for example, we can see a CommonJS-like module proposed for use on the server:

// io/File.js
export function open( path ) { ... };
export function close( hnd ) { ... };
// compiler/LexicalHandler.js
module file from "io/File";
 
import { open, close } from file;
export function scan( in ) {
    try {
        var h = open( in ) ...
    }
    finally { close( h ) }
}
module lexer from "compiler/LexicalHandler";
module stdlib from "@std";
 
//... scan(cmdline[0]) ...

Classes with Constructors, Getters, and Setters

The notion of a class has always been a contentious issue with purists, and we’ve so far got along with either falling back on JavaScript’s prototypal nature or through using frameworks or abstractions that offer the ability to use class definitions in a form that de-sugars to the same prototypal behavior.

In Harmony, classes have been proposed for the language along with constructors and (finally) some sense of true privacy. In the following examples, inline comments are provided to help explain how classes are structured.

Reading through, one may also notice the lack of the word “function” in here. This isn’t a typo error: TC39 have been making a conscious effort to decrease our abuse of the function keyword for everything, and the hope is that this will help simplify how we write code.

class Cake{

    // We can define the body of a class" constructor
    // function by using the keyword "constructor" followed
    // by an argument list of public and private declarations.
    constructor( name, toppings, price, cakeSize ){
        public name = name;
        public cakeSize = cakeSize;
        public toppings = toppings;
        private price = price;

    }

    // As a part of ES.next's efforts to decrease the unnecessary
    // use of "function" for everything, you'll notice that it's
    // dropped for cases such as the following. Here an identifier
    // followed by an argument list and a body defines a new method

    addTopping( topping ){
        public( this ).toppings.push( topping );
    }

    // Getters can be defined by declaring get before
    // an identifier/method name and a curly body.
    get allToppings(){
        return public( this ).toppings;
    }

    get qualifiesForDiscount(){
        return private( this ).price > 5;
    }

    // Similar to getters, setters can be defined by using
    // the "set" keyword before an identifier
    set cakeSize( cSize ){
        if( cSize < 0 ){
            throw new Error( "Cake must be a valid size - 
            either small, medium or large" );
        }
        public( this ).cakeSize = cSize;
    }


}

ES Harmony Conclusions

As we’ve seen, Harmony might come with some exciting new additions that will ease the development of modular applications and handling concerns such as dependency management.

At present, our best options for using Harmony syntax in today’s browsers is through a transpiler such as Google Traceur or Esprima. There are also projects such as Require HM that allow us to use Harmony modules with AMD. Our best bets however until we have specification finalization are AMD (for in-browser modules) and CommonJS (for those on the server).

Get Learning JavaScript Design Patterns now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.