In the world of enterprise JavaScript web development, the solutions being built, and the tools being used to build these solutions, are often written minimally to run in an ECMAScript 3 environment (aka ES3). You may wonder how this can be. After all, ECMAScript 5 was finalized in June 2011 and is supported by all modern browsers, including IE 9 (except for strict mode).
In this article, I'll examine why many, if not most, enterprise JavaScript developers still need to care about ES3 and several strategies for developers who want to use ES5/ES6 features today (as well as my reasons for not doing so myself).
Supporting IE 8, 7, and yes even 6, can still be a common requirement in the enterprise. Like it or not, agree with it or not, it's true. Enterprise requirements, even today, can require code to be written to run in IE 6, 7 and most commonly 8. In other words, using native ES5 or ES6 JavaScript features in production code is still a futuristic ideal in some enterprise environments.
As such, the code written and the tools used by enterprises developers typically must support ES3 environments. For example, most of the Kendo UI widgets (excluding mobile widgets) are tested and supported in IE 7+. Even a short investigation into most any JavaScript solution crafted for the enterprise developer will reveal the fact that most solutions are coded to ES3 standards.
This shouldn't be a surprise to anyone who has cut their teeth on enterprise work. However, what might be difficult to come to terms with is that many of the latest JavaScript tools (for example, Angular 1.3) are not exactly tested and developed to run perfectly, or at all, in ES3 only environments. At least not without some serious caveats, workarounds, and wishful polyfilling.
Now, enterprise tools are often perceived and revered as highly robust, top of the line, and time-tested, primed for only the most complicated of development problems. But, the reality is, most enterprise JavaScript code and enterprise tools are written and standardized on a specification that was finalized 16 years ago.
ES3 is certainly time-tested, but highly robust and enterprise-worthy, I fear, is a debatable proposition in the minds of modern JavaScript developers. After all, by design, the newly spec'd ES5 and ES6 features are the exact remedies missing in ES3, which deal with the complexities of modern day JavaScript development. In other words, the new JavaScript features are legitimately needed in our modern JavaScript world. And, without question, the features finalized 16 years ago alone are not up to the modern tasks of JavaScript development.
I would imagine this all sounds somewhat bleak to the ears of the average enterprise JavaScript developer. As a JavaScript developer myself, I think it is. I wish it was possible for everyone to use the newer JavaScript features immediately after being finalized and implemented. Browsers and the enterprise, be damned!
But what I wish to be the reality and what is the reality, are two different things. I suppose this is what leads people to either polyfilling in newer features, compiling their JavaScript code for older browsers, or giving up on client-side code and becoming a Node developer. Don't laugh, it happens. The client and the browser can certainly make a developer insane enough to jump ship into murky waters and swim for a better JavaScript runtime.
In all seriousness, the pragmatic approach for the enterprise developer writing code that runs in an ES3 environment, using ES3 tools, is to simply write ES3 code for production. All the while, I suppose, you can dream of the day that support for ES3 browsers can be dropped and modern ES5 or ES6 native code can be written instead. Of course, an enterprise dev has to wait for older browsers to die off, a longish engagement in the enterprise, for sure. So, day dreaming of the future can be a long venture in the enterprise.
I've personally never elected to use a comprehensive polyfill or a compile step to produce ES3 production code from ES5 or ES6 code. I've polyfilled a thing or two, but for me, I've never been able to overcome the complexity or overhead associated with a comprehensive polyfill or compiling routine (i.e. create ES3 production code from from ES5 or ES6 source code). The caveats, additional polyfills, limited usage, and incomplete features have mostly kept me at bay. For example, just go read all of the details around using ES6 feautres by way of the new 6to5 compiler.
This doesn't mean I haven't enjoyed, coded, and explored modern JavaScript features when given the chance. And, you should too. It simply means that when I am asked to support an ES3 environment, I have taken the safest path forward, which, I believe, means sticking with the tools and writing code that is as close to the ES3 metal as possible. After all, just because a tool requires an ES5+ environment doesn't mean that tool will live within the limitations of another third party polyfilling or compiling solution - that is, unless they decide to test and ship the tool with the necessary polyfills, which is not commonly done for obvious reasons (i.e. file size, lock-in, duplication etc.).
As I was saying, I haven't historically polyfilled or compiled. I believe constructing a JavaScript application is already an insanely complicated endeavor. Adding in even the slightest increase in complexity, overhead, or red tape (especially third-party red tape) to a large enterprise application should always result in a massive development win, which I just haven't found when considering the cost of adding in newer JavaScript features to an ES3 code base.
But, not everyone shares my views. And, certainly not everyone's threshold for additional complexity, or risk versus reward, is the same. So for the purpose of this article, I'm going to assume that, unlike me, you have decided that you want to use non-ES3 features from future specifications in an ES3 code base (note: I am not talking about polyfilling third-party code).
For the remainder of this article, we will investigate 3 strategies for using newer ES6+ JavaScript features in the enterprise.
Polyfilling an ES3 environment with as many ES5 and ES6 standard features as you can (even some ES7 stuff) is as simple as including the shim.js file from the core.js project in an HTML document.
Include shim.js as the first JavaScript file to run/parse in an HTML file and anything else written in JavaScript after that can make use of polyfilled features as if they were native (code examples below from core.js).
console.log(Array.from(new Set([1, 2, 3, 2, 1]))); // => [1, 2, 3]
console.log('*'.repeat(10)); // => '**********'
Promise.resolve(32).then(console.log); // => 32
setImmediate(console.log, 42); // => 42
You should be aware that core.js not only offers a comprehensive set of standard compliant ECMAScript polyfills, but also the following non-standard JavaScript features which are available by using core.js instead of shim.js.
The drop-in polyfill we just looked at, like it or not, adds newer features to ES3 environments at runtime. It will do so by any means necessary. If that's not what you want, then you have another option. The core.js project also offers an opt-in polyfill that gives you newer features, but these features can optionally be used by invoking the features individually. This is similar to what you'd expect from libraries like lo-dash that offer one global value (e.g. _
) which all features hang on.
To opt-in ES5 and ES6 features in your code, you can include the library.js file from core.js. You can then use newer ECMAScript features as needed by writing code accessing method features hanging of the 'core' global. Similar to what you see in the code example below (code example from core.js).
var log = core.console.log;
log(core.Array.from(new core.Set([1, 2, 3, 2, 1]))); // => [1, 2, 3]
log(core.String.repeat('*', 10)); // => '**********'
core.Promise.resolve(32).then(log); // => 32
core.setImmediate(log, 42); // => 42
ES5 and ES6 standard features can't be directly compiled into ES3 code (i.e. using old features to write newer, equivalent, features). Doing so would greatly limit the actual features that could be translated. What is plausible is to use a pre-compiling step to compile ES6 code to ES5 code and then use the drop-in shim.js polyfill to get you the final mile to ES3.
To do this use you could use the new 6to5 transpiler and create a pre-compile build task using gulp that turns ES6 into vanilla ES5. Then, according to the 6to5 documentation, to get a full ES6 runtime environment you'll also need to prep the runtime with the regenerate runtime, shim.js, and ES6 module loader polyfills (note: I am using client/shim.js from core.js, which includes support for ES3).
An HTML file containing 6to5 compiled code might look something like the following:
<!DOCTYPE html>
<html>
<body>
<script src="runtime.js"></script>
<script src="client/shim.js"></script>
<script src="es6-module-loader.js"></script>
<script>
System.parser = '6to5'; //set es6-module-loader.js to use 6to5
//6to5 compiled code here
</script>
</body>
</html>
Just remember, if you do this for production code, I recommend intimately investigating all the potential caveats involved.
In theory, if you have polyfilled or compiled+polyfilled code to allow for the use of newer JavaScript features in an ES3 environment, you should be safe to use third-party tools that only support ES5+ environments, right? Wrong!
About the only way you'd be totally safe to use a third-party tool in an ES3 environment is if the third-party code directly supports your exact polyfills/compiler. Otherwise, this can be a rather frustrating and timely assumption. Remember, all this hacking of new features into old environments comes with some caveats, complicated gotchas, and opinions. You shouldn't assume a third-party has dealt with these caveats and gotchas as you would (or have), unless you can confirm they align with your environment exactly. If what I'm saying is lost on you, just keep in mind that third-parties likely don't test ployfills or compiled output. It's mostly the case that third-party code requiring ES5+ parsing will assume a real ES5+ environment.
If I wasn't the author of this article and just a reader, I'd be asking myself, "Why are you telling me how to use newer features but not giving it a glowing recommendation?" This is a good question.
I wrote this article as a precursor to my next article that will discuss how to approach and learn newer ES6 features. I wanted to make sure, before discussing what is often the future for most developers, to establish a foundation for bring the future into the present. I wanted to make sure that the enterprise developer realized that if he/she was feeling brave, that shiny and new JavaScript features are available today even for production code. And, thus, making it relevant to be approaching, learning, and using ES6 today.
Header image courtesy of hackNY.org
Cody Lindley is a front-end developer working as a developer advocate for Telerik focused on the Kendo UI tools. He lives in Boise, ID with his wife and three children. You can read more about Cody on his site or follow him on Twitter at @codylindley.