Go to main contentGo to page footer

BazaarJS: Javascript preprocessors

BazarJS series, this time dedicated to the history, past and future of Javascript. We'll start by taking a look at the CoffeeScript and TypeScript pre-processors, then get to know ECMAScript 6 to find out whether we can finally start taking Javascript seriously.

Estimate reading 10 minutes

Javascript has a long history. In 1995, at the height of the browser war between Netscape Navigator 2.0 and Internet Explorer 1.0, Brendan Eich was entrusted with the task of creating a simple interpreted language for amateur programmers. This was to be an addition to the Netscape package, one which would compliment the Java applet designed for the use of professional programmers, into which the company was then investing everything it had. As Eich said:

Javascript had to "look like Java", only less so. It had to be Java's dumb kid brother or boy-hostage sidekick. Plus, I had to be done in ten days, or something worse than JS would have happened.

Taking this into account, it's fair to ask: how has it been possible for Javascript to survive years of (deserved) mockery of its limitations and idiosyncrasies to emerge at the present day where — according to the latest statistics from RedMonk — Javascript is the world's most widely-used language?

It certainly hasn't been a straight road, and more than once attempts have been make to dispose of the language entirely, replacing it with something more “serious”. To give a concrete example, the teams behind the creation of the first successful client-side web applications in history — Gmail and Google Maps — certainly did not see Javascript as a faithful ally.

The tools released during those years — GWT, Cappuccino, Pyjamas — are a clear illustration of the fact that anything was possible, except for writing complex web applications in Javascript. It was much better to work using desktop paradigms such as Java, Objective-C or Python and delegate, to a tool of some kind, the work of “compiling” the final result to a web native technology. Javascript-as-bytecode, that is to say, and nothing more.

CoffeeScript

Years have passed, and we have started, tentatively, to have some confidence in this language. Its approach to development that has become ever more “native” and it has broken away from the old logics of desktop development.

In 2009 the publication of CoffeeScript by Jeremy Ashkenas saw the unofficial birth and subsequent growth of a new approach to Javascript, one which was less negative and pessimistic in the approach it took to the language. This change in direction was well explained in the opening words of the project itself. So, what is CoffeeScript?

CoffeeScript is a little language that compiles into JavaScript. Underneath that awkward Java-esque patina, JavaScript has always had a gorgeous heart. CoffeeScript is an attempt to expose the good parts of JavaScript in a simple way.

The golden rule of CoffeeScript is: "It's just JavaScript". The code compiles one-to-one into the equivalent JS, and there is no interpretation at runtime.

In other words? Javascript isn't the best language ever invented, don't make fun of us... but if we take a look at ECMAScript 5, or even the latest edition of the Javascript standard already implemented in all modern browsers, we've got to admit that it isn't that bad either. Applying some surgical modifications to the language to fix a few of the macroscopic errors resulting from the excessive haste in which the project was put together we'll be able to get something that's both good and usable, all the while keeping “close” to the native technology of the web!

How is it possible to apply these surgical modifications? As in the case of GWT and similar technologies, by making developers write in another language... but this time a language that is extremely similar to Javascript, capable of generating Javascript code that a programmer will find perfectly intelligible, and is as idiomatic as possible.

Which type of “errors” does CoffeeScript try to fix? Principally those relating to syntax. Javascript offers us very few of the comforts that most other, more evolved, languages do:

  • string interpolation;
  • multi-line strings;
  • default arguments;
  • splat operators;
  • list comprehension;
  • unstructured assignment;
  • OOP classes.

It's also extremely verbose... all a matter of repeating brackets, dots and commas, this, function and return!

class Greeter
  constructor: (@person, options = {}) ->

  greet: (person) ->
    fullName = "#{firstName} #{lastName}"

    if person.name is "Stefano Verna"
      """
      Hello #{fullName},
      This is a multiline string, wow!
      """
    else
      "#{fullName}?! The hell are you?"

person =
  firstName: "Stefano"
  lastName: "Verna"

new Greeter(person).greet()

CoffeeScript offers an elegant solution to each one of these problems, introducing — where necessary — some pretty heavy modifications to the language:

  • white-space significance (brackets are out, Python-style indentation is in);
  • implicit return (everything is an expression);
  • _arrow-functions_: a new, more succinct syntax for defining functions (->);
  • _fat-arrows_: a variation on the arrow-function designed to force an automatic binding on current scope at the moment of definition of the function itself (=>).

The result? Faster, more enjoyable development and clearer code that gives a better understanding of the programmer's intentions; in addition to producing ECMAScript 3 compatible code (IE6 proof).

How to use CoffeeScript

It's possible to compile CoffeeScript in Javascript by using the npm coffee-script package. Once it's installed, a coffee binary is available, from which you have the option of producing source-maps:

npm install -g coffee-script
coffee --compile --map --output lib/ src/

Obviously, plugins for the principal task runners (grunt-contrib-coffeescript, gulp-coffee) are available, making it possible for compilation to be integrated into a larger build process.

The rise and fall of CoffeeScript

Inevitably, the release of CoffeeScript gave rise to years of perplexity and religious warfare over the question of whether it was really necessary to write code in a language that was so young, and so similar to Javascript, merely in order to add a bit of syntactical sugar to the coding process. Despite this, those communities more open to innovation — such as the Ruby/Rails one — immediately accepted the language and added it to their standard toolkit.

Setting aside personal opinion, the fact of the matter is that CoffeeScript has grown enormously over time. The highpoint was 2013, when it reached 17th place in the rankings of the most used programming languages: an evident symptom of the need for modernization and renewal present in the Javascript community.

Since 2013, however, the use of CoffeeScript has undergone a slow but progressive process of decline. The reason is pretty obvious to see: ECMAScript 6.

ECMAScript

So, what exactly is ECMAScript? The formal definition of the Javascript language is the responsibility of the ECMA TC39 committee. Javascript is one of the implementations of ECMAScript, as are a number of other languages, such as ActionScript, for example.

The progression of the ECMAScript standard, like the progression of Javascript itself, has not been straightforward. ECMAScript 3 was released in 1999 and the subsequent release of the language — ECMAScript 4 — was supposed to include major modifications. The completion date of 2008 was, however, completely ignored, and the release was eventually entirely abandoned due to political differences regarding the complexity of the language that would have been produced.

Following this failure, the committee decided to work on a less ambitious release, containing more incremental modifications: the first concrete consequence of this was ECMAScript 5, which was released in 2009 and which we find implemented in our browsers (IE9+) today. The most important modifications to be found in this version are Getters/setters and "strict" mode.

The next step planned by the committee was the definition of ECMAScript 6, whose release was initially planned for 2013 but which would ultimately see the light of day towards the middle of 2015.

And with ECMAScript 6, things start to get really interesting.

ECMAScript 6

Describing in detail all of the changes to the language present in the new standard would be an arduous task, and perhaps also a futile one, considering that a number of excellent resources designed for this purpose are already available:

Straight away, we can see that almost all of the syntactical aids found in CoffeeScript have been faithfully reproduced in ES6, while retro-compatibility with the language as it has existed up until the present day has been maintained. ES6 is, in all respects, an enhanced super-set of ES5.

Let's take a look at some of the more noticeable changes that have been made to the language:

Functions: splats, default arguments and fat-arrow binding

var foo = (filter = 'all', options = {}) => {
  // ...
};
var bar = (first, second, ...others) => {
  // ...
};

var object = {
  init() {
    $("a").click((el) => this.handleClick());
  },
  handleClick() {
    /* ... */
  }
};
  • It is finally possible to set default values for the arguments of a function;
  • Parameters can be globbed using an ad hoc syntax (...), eliminating the need to make use of object arguments;
  • The keyword function is optional: the short-cut functionName() {} can be used within the object definition;
  • Using the fat-arrow syntax (=>) it's possible to implement the forced binding of a function for the current value of this, with automatic return; Comprehensions, destructured assignments and lexical scoping

Comprehensions, destructured assignments e lexical scoping

let collection = [ 1, 2, 3 ];
let [first, second] = collection;

for (el of collection) {
    // ...
}

let generatePoint = (x, y) => { x, y }

let { x, y } = generatePoint(10, 20);
  • The creation and extraction of data from object arrays has been vastly simplified, by means of destructured assignment;

  • It's possible to iterate through (amongst other things) the elements of an array, by means of the of construction;

  • The use of the var keyword has now been officially discouraged, in favour of let. The scoping of variables in Javascript has always been bizarre, to the point that it has been necessary to coin a new term to describe it (*variable-hoisting*). The let keyword resolves this problem, managing scoping with the same mechanisms used by Ruby and most other languages;

Object-oriented classes

class Lion extends Animal {
  constructor(name) {
    this.name = name;
    this.pos = 0;
  }

  move(meters) {
    this.pos += meters;
  }
}

var lion = new Lion("Alex");

Javascript has always been a language with prototypal inheritance, a rather unfamiliar concept for those arriving from languages with classical inheritance. In practice, in the course of every project/library of medium dimensions, it becomes necessary make use of (or implement ex-novo) small JS libraries designed to recreate the behaviour of a typical OOP class.

ES6 offers a series of constructions (class, extends, super) that make it possible get the same result, but with greater clarity of intent and uniformity of structure.

String Interpolation and block strings

var fullName = `${firstName} ${lastName}`;

var greeting = `
Hello ${fullName}! This is a multi-
line string!
`;

Last but not least, there's backtick definition of strings, which makes it possible for these to be extended across multiple lines and supports the interpolation of variables.

...and that's just the tip of the iceberg!

The changes present in ES6 cannot be reduced to these kind of "cosmetic improvements". Rather, their substance lies in the introduction of concepts that have so far been entirely absent, and which have the capacity to revolutionize the idioms and possibilities of the language itself:

  • Meta-programming: the Proxy object provides functionality similar to that of Ruby's classic method_missing, allowing for the dynamic creation of the methods and properties of objects (DSL, anyone?);

  • Symbols: ES6 introduces a new type of primitive, Symbol, which can be used as an unambiguous, private, property-name;

  • Generators: Ruby'syield arrives in Javascript, allowing for the creation of (amongst other things) iterable objects;

Is it possible to use ES6 today?

At present, browser support for ES6 is very limited: Firefox is currently the browser in which the greatest number of its features have been implemented, but there is still a long road ahead before it will be possible to take complete compatibility over across browsers for granted.

There is, however, some good news. Projects like Traceur e Babel and Babel are capable of transpiling ES6 code into simple ES5, allowing for the use of its new functionality today with the promise that — in the not so distant future — it will be possible to eliminate the pre-compilation process entirely.

6to5, in particular, seems to be the most promising such project, as it both enjoys the greatest amount of support among the community and has the highest level of compatibility with the standard.

It is possible to use Babel in stand-alone mode, REPL, by using the usual task runners (grunt-6to5, gulp-6to5), and under the transform format with Browserify and Webpack (6to5ify, 6to5-loader). It is even possible to use it in Rails/Sprockets, by means of the sprockets-es6 gem.

TypeScript

TypeScript, a superset language of ES5 created by Microsoft, is worthy of particular mention. In addition to making it possible to create classes with classical inheritance, the principal characteristic of the language is that it allows for static-typing and compile-time checking of Javascript code by means of annotations:

// add.ts
function add(left: number, right: number): number {
  return left + right;
}

add("Foo");
add(1);

var result = add(1, 1);
result.foobar;
$ npm install -g typescript
$ tsc add.ts

add.ts(5,1): error TS2346: Supplied parameters do not match any signature of call target.
add.ts(6,1): error TS2346: Supplied parameters do not match any signature of call target.
add.ts(9,8): error TS2339: Property 'foobar' does not exist on type 'number'.

For defining complex structures, the approach toward typification works by means of the concept of duck-typing: a process in which the semantics of an object are determined by the combination of its methods and properties, rather than by the fact that it is an extension of a particular class or an implementation of a specific interface:

interface ObjectWithTitle {
  title: string;
}

function printTitle(titledObject: ObjectWithTitle) {
  console.log(titledObject.title);
}

var article = {
  title: "This is awesome!",
  publishedAt: new Date(),
  content: "Foo bar"
};

printTitle(article);

It isn't necessary to make major investments in order to start using TypeScript, particularly considering:

  • The optionality of static-typing: everything that does not provide annotations is accepted as an instance of the any type.

  • The possibility of using a type definition header, separated from the rest of the code, allowing for the addition of a layer of typification to third party libraries1.

Our choice?

Here at Lean Panda, we've always been great supporters of tools like Sass and Slim, which can make our daily work more enjoyable, less repetitive and less error-prone. CoffeeScript fulfils all of these requirements perfectly, and it is for this reason that we have been using it in-house for almost four years (basically from its initial launch until today).

Jeremy Ashkenas' project can be said to have been 100% successful in achieving its objectives. It has even gone so far as to influence the work of the ECMA committee, which itself deserves acknowledgement for having been able to react quickly to external feedback.

We have so far had the opportunity to work on only one ES6 project, which was a great success. All of the annoyances and pains that users of the language used to be warned about have disappeared. We also found that we didn't miss being able to work in CoffeeScript at all. We had the sensation — which we had never before had with Javascript — of working with a mature language capable of great expressiveness. One able to support us adequately in the development of complex client-side applications.

TypeScript is also an interesting project: the type-checking phase of compilation has the potential to reduce the number of "interfacing" errors among objects. To be honest, the ideal thing would be to be able to use the two languages in conjunction with one another... unfortunately TypeScript is a superset of ES5, meaning that they are currently incompatible.

Lean Panda are used to working with dynamic typification and error-checking our code using the most complete possible integrated test suite. Forced to choose between the concrete advantages of ES6 in terms of comfort and the theoretical advantages of TypeScript on a minor source of errors, we would without doubt choose ECMAScript 6.

Fortunately, the TypeScript team have already announced, amongst other things, their intention to align the language with ES6 in the forthcoming version 2.0. This means that it should only be necessary to wait a few months to be able to road test TypeScript in combination with ES6.

In the coming weeks we will continue to look into different areas of functionality that ES6 provides, with dedicated posts on the most interesting and innovative of these.

That's all for today... the next episode? Framework JS!

The moment has finally arrived. We can start to warm up the motors of religious warfare. The next episode with deal with the world of client-side frameworks and take an in-depth look at the pros and cons of the three principal contenders: Angular, Ember.js and React.

Follow us on Twitter or subscribe to our feed RSS to keep up to date!

Did you find this interesting?Know us better