Sencha Inc. | HTML5 Apps

Blog

Toward Modern Web Apps with ECMAScript 6

August 13, 2013 | Ariya Hidayat

ECMAScript, the official name for the language we all know as JavaScript, has enjoyed tremendous success over the last couple of years. With convergent standard support, performance boosts from modern JavaScript engines, as well as its foray into the server-side stack, ECMAScript has gained significant traction and redefined the scope of HTML5 applications. The final requirement for world domination is the modernization of its syntax and run-time, which is coming in ECMAScript 6 and is the subject of this post.

To understand the language progress within ECMAScript 6, first we need to understand how it is developed. The official ECMAScript specification, also known as ECMA-262, is defined by a standard body called Ecma International. The TC39 group within ECMA is responsible for formalizing the language specification (TC stands for Technical Committee). Every browser vendor, as well as other interested parties, has one or more representative members in this committee. Those members are tasked with championing features and additions to the next version of the language. From time to time, TC39 members meet to review the recent progress and to finalize the formal specification. Often, ideas are also discussed with the public in the es-discuss mailing-list.

The ECMAScript 6 specification is still in the draft stage. There have been many draft revisions so far, with the latest one coming out as recently as July 15th. Keep in mind that the concepts and the examples illustrated in this article follow the latest specification draft and its related discussions at the time of writing, but there is still always a possibility that some syntax or function name may change in the near future before the finalization of the specification.

“I Can See Clearly Now”

It is often said that “Code is written once but read many times”. In fact, many of us who spend countless hours in the code review process realize how important it is to have clear and unambiguous code. In addition, crystal clear syntax makes it easy for a new team member to understand the code and to avoid common pitfalls.

A common mystery for a novice is the following construct:

 
// ES 5
function inc(x, y) {
  y = y || 1;
  return x + y;
}
 

The use of the second argument, y, can be confusing. Its value includes a logical OR with the constant 1, so that if y is undefined (or, more precisely, when y is falsy), it has the value of the constant 1. For example, if the function is called with only one argument, such as inc(4), the return value is 5 (which is 4 plus the default value of 1 for y). Such a seemingly clever hack is buggy (passing y as 0 produces the wrong outcome), error-prone (what happens if bitwise OR is accidently used instead of logical OR), and totally meaningless to those who are not familiar with the technique. There are other variants which solve these issues, but the problem remains the same: it is still a run-time workaround.

ECMAScript 6 solves this issue by having a built-in feature for the default parameter value, allowing the previous construct to be rewritten in the following way, which should make a lot of sense to those familiar with other programming languages:

 
// ES 6
function inc(x, y = 1) { return x += y; }
 

Another common pitfall for beginners is the fact that variables have function scope instead of block scope. If someone is already used to other “curly-brace” programming languages, this scope difference can come as a shock. In addition to that, variable hoisting confuses the situation even further: a variable may exist in a scope although it has not been initialized.

Take a look at the following example where the variable N within the conditional body shadows the outer N. Since N is function-scoped, declaring a new N within the body of the if statement does not have any effect; it is still the same N and thus the original value is overwritten.

 
// ES 5
function doSomething() {
  var N = 5;
  if (someCondition) {
     var N = 10;
     doSomethingElse(N);
  }
  console.log(N); // 10
}
 

This is a rather trivial example and hence the problem can be spotted immediately. But in a large code block which uses several closures, such a sloppy name conflict may lead to obscure bugs.

ECMAScript 6 introduces the concept of lexical block scope, where a variable or a function is enclosed within the closest curly braces. The problem in the above example can be solved elegantly using the new let keyword.

 
// ES 6
function doSomething() {
  let N = 5;
  if (someCondition) {
     let N = 10;
     doSomethingElse(N);
  }
  console.log(N); // 5
}
 
A sibling to let is const. It works the same as let, ncluding the block scope, but it requires an initialization value. As the name implies, const has a constant binding and thus replacing its value triggers an error. Update: As mentioned by Olov Lassus, const reassignment is an early syntax error at parse time.

 
/// ES 6
'use strict'
const limit = 100;
limit = 200; // SyntaxError: Assignment to constant variable.
 

The major use case for const is to safeguard important constants from being overwritten accidentally.

ECMAScript 6 also has some new features that reduce the ceremonial aspect of coding. Concise method definition provides a compact way to define a function within an object, making it symmetrical to the way its properties are defined. As evidenced from the following example, there is no need to use the keyword function anymore.

 
// ES 6
var Person = {
  name: 'Joe',
  hello() { console.log('Hello, my name is', this.name); }
};
 

A function that serves as a callback (not part of an object) can enjoy a shortcut notation as well via the use of the arrow function. For example, the following code computes the squares of some numbers:

 
// ES 5
[1,2,3].map(function (x) {
  return x * x;
});
 

The ECMAScript 6 version can be shorter:

 
// ES 6
[1,2,3].map(x => x * x);
 

With the use of the arrow function (=>), we no longer need the function and return keywords. The last expression within the function body becomes the return value. Note that, if the function needs to accept two parameters or more, those parameters must be enclosed with parentheses.

The new spread operator in ECMAScript 6 can be used to reduce the run-time boilerplate code. It is quite common for a seasoned developer to use apply creatively, in particular when an array needs to be used as the list of function arguments. An example of such usage is:

 
// ES 5
var max = Math.max.apply(null, [14, 3, 77]);
 

Fortunately, this kind of confusing construct will not be the norm anymore. The spread operator “…,” as the name implies, spreads the forthcoming array into the proper arguments for the said function.

 
// ES 6
var max = Math.max(...[14, 3, 77]);
 

Another use of the spread operator is for rest parameters. This is the exactly the opposite of the example above: now we must convert variable function arguments into an array. To appreciate the importance of this, take a look at the following code:

 
// ES 5
store('Joe', 'money');
store('Jane', 'letters', 'certificates');
function store(name) {
  var items = [].slice.call(arguments, 1);
  items.forEach(function (item) {
    vault.customer[name].push(item);
  });
}
 

Here we have a vault in the bank and we want to store an unlimited amount of belongings to the corresponding customer’s safe deposit. With such an API, the function store needs to cast a magical spell with the special object arguments, so it can convert the passed belongings into an array and then push the array elements to the right location. This is far from being clear, it is certainly a code smell.

That run-time hassle is finally gone in ECMAScript 6 with the use of the spread operator for the rest parameters, as demonstrated in the following snippet, re-written for ECMAScript 6:

 
// ES 6
store('Joe', 'money');
store('Jane', 'letters', 'certificates');
function store(name, ...items) {
  items.forEach(function (item) {
    vault.customer[name].push(items)
  });
}
 

For those who love functional programming, the new array comprehension syntax will be strikingly familiar. Basically, this permits a more natural construct compared to the use of Array’s higher-order functions. For example, the following simple iteration with Array.prototype.map:

 
// ES 5
[1, 2, 3].map(function (i) { return i * i });
 

will have a different look when rewritten using array comprehension:

 
 // ES 6
[for (i of [1, 2, 3]) i * i];
 

Obviously, array comprehension supports filtering, much like the use of Array.prototype.filter. Compare the following two variants:

 
// ES 5
[1,4,2,3,-8].filter(function(i) { return i < 3 });
 
// ES 6
[for (i of [1,4,2,3,-8]) if (i < 3) i];
 

Even better, array comprehension can handle multiple iterations, much like nested loops in the imperative style. For example, the following one-liner produces all possible chess positions (a1 to h8):

 
// ES 6
[for (x of 'abcdefgh'.split('')) for (y of '12345678'.split('')) (x+y)];
 

Run-Time Renaissance

In addition to the above-mentioned incremental improvements to the language syntax, ECMAScript 6 also offers some run-time changes that improve the life of web developers. Two things which relate directly to the development of large-scale applications are class and modules.

The new class syntax is designed to be concise and easy to understand. The following example code shows how to create a simple class:

 
// ES 6
class Vehicle {
  constructor(color) {
    this.color = color;
    this.speed = 0;
  }
  drive() {
    this.speed = 40;
  }
}
 

A class method with the name constructor has a special meaning, it acts as (surprisingly) the class constructor. Other than that, everything resembles the familiar concept of class as in many other programming languages.

Written in today’s ECMAScript 5, this Vehicle class would look like:

 
// ES 5
function Vehicle(color) {
  this.color = color;
  this.speed = 0;
}
 
Vehicle.prototype.drive = function() {
  this.speed = 40;
}
 

Use the keyword extends to derive a new class from the base class, as illustrated below. Note the use of super within the constructor, which provides a way to call the constructor of the parent class.

 
// ES 6
class Car extends Vehicle {
  constructor(brand, color) {
    super(color);
    this.brand = brand;
    this.wheels = 4;
  }
}
 

Again, this subclassing is just a syntactic sugar. Doing it by hand would have resulted in the following equivalent code:

 
// ES 5
function Car(brand, color) {
  Vehicle.call(this, color);
  this.brand = brand;
  this.wheels = 4;
}
 
Car.prototype = Object.create(Vehicle.prototype);
Car.prototype.constructor = Car;
 

The built-in support for handling modules is extremely important. Modern web applications grow in complexity and to date, structuring various parts of the application to be modular required a lot of effort. There are libraries, loaders, and other tools to ease the burden but developers have had to sink time in learning these non-standard solutions.

Module syntax is still being discussed and finalized. To get a taste of what is possible, take a look at the following fragment which forms a simple module.

 
const HALF = 0.5;
export function sqrt(x) {
  return Math.exp(HALF * Math.log(x));
}
 

Assuming this code lives in a file (or module) called mathlib.js, we can later use the function sqrt from another place. This is possible because that function is exported, as indicated by the keyword export. While the above example shows an exported function, there are many other values that can be exported: variables, objects, and classes. Also, note that the constant HALF is not exported and hence it is only visible within this module.

How do we use the module? From another source file, we can simply import the function using the keyword import following this syntax:

 
import { sqrt } from 'mathlib';
console.log(sqrt(16));
 

While the above use case suffices for the common cases, ECMAScript 6 module support goes well beyond this. For example, it is possible to define inline modules, rename exported values, declare default export and import. In addition, there will be a loader system to facilitate dynamic module resolution and loading.

Another interesting run-time feature of ECMAScript 6 is the generator function, which is very useful for suspending and resuming program execution within a function. The use of generators, particularly for sequential operations, is an alternative to the asynchronous approach using callback which has led many developers into “callback hell”.

Within the generator function, yield suspends the execution and next resumes it. The following fragment illustrates a very simple generator:

 
function* foo() {
  yield 'foo';
  yield 'bar';
  yield 'baz';
}
 
var x = foo();
console.log(x.next().value); // 'foo'
console.log(x.next().value); // 'bar'
console.log(x.next().value); // 'baz'
 

Back from the Future

Browsers like Firefox and Chrome (with an experimental flag) are starting to support some of the above mentioned ECMAScript 6 features, but ECMAScript 6 is still far from universally available in its entirety. Fortunately, there is a possible solution to that, namely using a transpiler which converts code written in ECMAScript 6 into something that today’s browsers can execute.

A popular choice for an ECMAScript 6 transpiler is Traceur, a project from Google (GitHub: github.com/google/traceur-compiler). Traceur supports many features of ECMAScript 6, it generates translated code which runs quite well on modern browsers.

Another alternative is to start adopting TypeScript from Microsoft (http://www.typescriptlang.org/). TypeScript is not necessarily equivalent to ECMAScript; it can be viewed as a superset of ECMAScript. TypeScript supports popular features which are part of ECMAScript 6 but at the same time it also adds optional static typing.

Traceur and TypeScript are open-source projects that can be used in many environments. Besides these two projects, there are also other transpilers with a more specific purpose such as es6-module-loader (dynamic module loading), defs.js (only for let and const), and many others.

Final Words

With the modernization of the syntax, richer built-in objects, as well as being compilation-friendly, ECMAScript 6 paves the way to a brighter future for building large-scale web applications. We are excited to see where ECMAScript 6 is heading, and we are evaluating the ways we may be able to use the changes in our frameworks!

P.S: Special thanks to Axel Rauschmayer for his technical review of this article.

There are 14 responses. Add yours.

Fermuch

8 months ago

Where it says
[1,2,3].map(x => x * x);
It should be
[1,2,3].map(x => x * x);


Also, great post. I expect great improvements with ECMAScript 6.

Jennifer Liu Sencha Employee

8 months ago

@Fermuch, it’s fixed now. Thanks for the catch!

Ivan Jouikov

8 months ago

Nice one, Ariya! Love the new class system; no more prototype messiness. Competition to Ext/Touch class system though? smile

Westy

8 months ago

Good post Ariya; interesting to see what is coming.

Shame that given browser inertia it’ll take years for support of any revolutions in JavaScript to be commonplace, but we can always hope.
Maybe Google will stop blocking access to their search for non-current browsers one day soon - we can only hope!

Cheers,
Westy

Corey Butler

8 months ago

Nice sneak peak. I am looking forward to classes/modules landing.

It would be nice to see mention of proxies too, since there is some support for them in V8 (—harmony flag) as well as a few pretty decent polyfills (harmony-reflect). I’ve been using proxies in my Node.js platform for a while now - specifically for data models. Anytime the value of a object attribute is changed, it fires an event, which is great for things like real-time data integrity checks. It takes a little getting used to, but I think it’s pretty sweet.

halcwb

8 months ago

Funny, I have been programming in vb6, c, vb.net, c#, clisp, python, f#, javascript and over time all these languages seem to grow together. The list comprehensions and the generators are directly borrowed from python, while the lambda notation resembles c# or f#. I think this will definitely improve the language. Now hope they go for enabling immutability!

AM

8 months ago

So, uh, ECMA6 looks like python but is still kinda halfassed?

Awesome.

halcwb

8 months ago

By the way, if you go to this page: http://kangax.github.io/es5-compat-table/es6/ You c.an see that a lot of these features can tried out.

Matthieu Larcher

8 months ago

Great article! Thanks for this sneak peek into some interesting features coming to ES.
Let’s hope browsers don’t take too long to get up to pace.

Kris Zhang

8 months ago

I didn’t like something like => extends const export let;
JavaScript shouldn’t be so complicated, these features can be implemented in external package, like coffee script.
Please keep the simplicity of JS.

trans

8 months ago

Note sure why this:

  [for (i of [1,4,2,3,-8]) if (i < 3) i];

would be thought better than:

  [1,4,2,3,-8].filter(i => i < 3);

Ethan

8 months ago

ES6 makes JavaScript = VIMScript + Python + PHP

Hola, will you still fall in love with it?

Tom Coulton

8 months ago

We translated this blog post here: http://www.xenophy.com/javascript/8447

Tom Coulton

8 months ago

Forgot to say, the translation is in Japanese….

Comments are Gravatar enabled. Your email address will not be shown.

Commenting is not available in this channel entry.