Introducing React ReExt – Sencha Ext JS Components in React! LEARN MORE

Toward Modern Web Apps with ECMAScript 6

August 2, 2013 133 Views
Show

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 false), 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, including the block scope, but it requires an initialization value. As the name implies, const has a constant binding and thus replacing its value has no effect:

// ES 6
const limit = 100;
limit = 200;
console.log(limit); // 100

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 that 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
) 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
) 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
;

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 subset 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. Beside these two projects, there are also other transpiler 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 to 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.

Show
Start building with Ext JS today

Build 10x web apps faster with 140+ pre-build components and tools.

Latest Content
Discover the Top 07 Architecture Patterns used in Modern Enterprise Software Development

Developing software without an architecture pattern may have been an option back then. However, that’s…

JavaScript Design Patterns: A Hands-On Guide with Real-world Examples

As a web developer, you know how popular JavaScript is in the web app development…

Virtual JS Days 2024のハイライト

2024年2月20日~22日、第3回目となる「Virtual JavaScript Days」が開催されました。JavaScript の幅広いトピックを採り上げた数多くのセッションを実施。その内容は、Senchaの最新製品、ReExt、Rapid Ext JSまで多岐にわたり、JavaScriptの最新のサンプルも含まれます。 このカンファレンスでは多くのトピックをカバーしています。Senchaでセールスエンジニアを務めるMarc Gusmano氏は、注目すべきセッションを主催しました。Marc は Sencha の最新製品「ReExt」について、詳細なプレゼンテーションを実施。その機能とメリットを、参加者に理解してもらうべく詳細に説明しました。 カンファレンスは、Senchaのジェネラルマネージャを務めるStephen Strake氏によるキーノートでスタートしました。キーノートでは、会社の将来のビジョンについての洞察を共有しています。世界中から JavaScript 開発者、エンジニア、愛好家が集まるとてもエキサイティングなイベントとなりました。これは、JavaScript…

See More