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.
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
);
}
}
}
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"
);
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"
);
});
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]) ...
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
;
}
}
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.