EcmaScript 6 (ES6), aka ES2015, is the latest JavaScript standard and its features are increasingly being supported by modern browsers. Despite the fact that it's currently 2016, some features of ES6 are still not supported by any modern browsers. This lack of complete browser support for ES6 is the main reason why I stuck with ES5. Aside from browser support, my other issue with ES6 was that its features seemed to be almost entirely superfluous sugar.
Browser Support
Most of us who write JavaScript want to see the results of our code as quickly as possible. My old workflow was to set my tests to auto-watch and then make edits to my code. When I wanted to see end-to-end results I could switch to my browser and hit refresh. My web applications would load all of the source raw, as is, straight out of my project directory. This let me see exactly what my code was doing and I could debug or trace source code involved without worrying about source mapping.
Since ES6 support varies across browsers, tools like Babel and TypeScript are required to transpile ES6 code into ES5 code that a majority of browsers can understand. This intermediate step not only takes time, it also transforms the code. The result is that any time you need to debug an issue, you'll have to map the code back to its source first. I was not interested in making debugging harder than it needed to be. Speaking of debugging, you can now install Augury for working with Angular 2.
Befriending Transpilation
Despite my distaste for transpilation, there were a few problems with my arguments and assumptions.
Minification is Transpilation
The funny thing about my transpilation complicating debugging argument is that production applications should always be minified. While pedants won't consider minification to truly be transpilation, the effects are similar in the sense that outputted code needs to be mapped back to source.
In my personal experience, the hardest bugs to fix are those noticed in production; not development. Since production applications are always minified the chore of mapping back to source is always going to complicate debugging. That is not a good reason to complicate development, though. Fortunately, source mapping software has matured to the point where it is transparent to the developer.
There was a time when mapping minified or transpiled source back to its original sources involved extra work. Over the last few years, this process has been dramatically streamlined. These days, tracing a problem from a transpiled or minified source is largely transparent. This effectively renders the transpilation complication argument as moot.
That being said, there is a time cost to transpiling.
Modern Bundlers Are Faster Than F5/⌘R
Aside from ease of debugging, feedback time was my other primary argument against transpilation. In my "classic" workflow, all I had to do was hit F5 or ⌘R and my application would completely reload. This allowed for quick feedback and it is important for developers to get quick feedback.
It's important to note that in this classic workflow I would have to switch tabs and then press a button to trigger a reload. I am highlighting this for two reasons:
- I was required to manually press a button after switching applications.
- Application reload would not start until after refresh was hit.
It is also worth noting that this method involved reading files from disk which will become relevant in a moment.
The modern alternative to my "classic" workflow is to use a tool like WebPack or JSPM to handle bundling. These tools actually integrate with the developer's web browser and can start reloading the application when changes are saved. This means that an application will start reloading before the developer even switches out of their editor. This often means that by the time the web browser comes into focus the updated application can be used.
Modern bundlers have other benefits too. One is that they can rebuild your application incrementally which can dramatically reduce transpilation time so that it doesn't slow down development. Another is that they can serve their changes out of memory as opposed to from disk. This also speeds up turn around time.
In my "classic" workflow scripts would have had to be loaded through script tags and done so in the correct sequence. This works well for small applications but ends up being surprisingly time consuming with larger projects. Such an approach also requires some form of "manifest" that would need to be maintained as the project grows. However, this is unnecessary when using a modern transpiler.
Sugar Is Sweet
Many of the features found in ES6 seem like sugar - shortcuts that make working with the language "sweeter". Since ES6 is a superset of ES5, it's very easy to dismiss these features as being non-critical. While the features are not critical, they do make code easier to read and are very hard to give up once you start using them.
Consider the following ES5 function that adds a variable number of arguments:
function sum() {
var args = Array.prototype.slice.apply(null, arguments);
return args.reduce(function (state, arg) { return state + arg; }, 0);
}
console.log(sum(1, 2, 3)); // outputs 6
Readers familiar with JavaScript's arguments object will find this function straight forward. There are three concerns to note:
- Reading from the arguments object causes some JavaScript engines to de-optimize the function.
- The arguments object looks like an Array but does not strictly act like one, meaning developers need to convert them to proper Arrays before they can
be used. - Converting arguments adds an extra line of code that needs to be understood by a human maintainer.
The ES6 version of the sum function is much more terse and straightforward:
const sum = (...args) => args.reduce((state, arg) => state + arg, 0);
console.log(sum(1, 2, 3)); // outputs 6
Beyond Sugar: First Class Modules
At a first glance, ES6 modules can seem like sugar for community driven module solutions like CommonJS, AMD, etc. ES6 modules are actually a first class feature and provide functionality not easily possible with traditional ES5. Aside from a new syntax, the aspect of ES6 modules that makes them a full blown addition to the language is how they preserve module bindings"ES6 Modules Preserve Bindings".
Community driven module approaches did not have the luxury of defining new language specifications. Consequently, they typically exposed modules as plain old JavaScript objects. This had a number of ramifications notably the fact that properties could be bound to modules at any time. ES6 modules expose objects that have properties with immutable bindings that are made at declaration time.
Conclusions
ES6 provides a robust programming experience not possible with earlier versions of the language. Despite seeming like a hindrance, transpilation is actually a worthwhile feature. Transpilation is actually a necessary step in a continuous integration workflow since all production applications are minified. Despite being initially skeptical, if not outright hostile towards the idea of a workflow dependent on transpilation I have become an ES6 convert.