Do you - probably a JS developer reading this article right know - remember ECMAScript 6 (ES6)? Released in 2015 ES6 standard for JavaScript language is what kick-started JS rapid development that can be appreciated today. Just face it - ES6 made JS fun to write again. With added functionality and some syntactic sugar, it made the web a place for more and more advanced websites and web apps. ES2016 (ES7), ES2017 and up only made it better with even more features (e.g. async / await) in a yearly release cycle. It also greatly impacted the JS ecosystem with frameworks like Angular, React and Vue becoming even more popular and dependent on. But ECMAScript is just a standard that needs to be implemented by different browsers' JS engines (e.g. V8 from Google Chrome) and this is where things start to get less fun. Naturally, the fact that the standard has been defined, doesn't mean that it will be implemented right away. Old browsers' versions obviously will never get new features and on the newer ones, it may take a long time to implement them.

Enter the compilers era

So, not everybody wants to wait this amount of time just to experience easier, better and modern coding solutions. That's why the JS compilers were created. These transpilers (term used interchangeably, but in my opinion better-describing the purpose of these tools) just take the code written in modern-style and change it to one compatible with more older browsers. This works almost all the time for new syntactic sugar but not really for modern language features and API, but it's enough to satisfy the needs of many developers. The most popular and well-known one is Babel - compiler implementing the latest and greatest ES-Next (a term for all new and up-coming ES versions) features ASAP and most likely a good friend of yours. Of course, solutions like this one left the JS community a bit divided between the ones wanting modern features and ones wanting performance. And here comes the point of this article. You can have code with great performance or code with modern syntax using transpiler, or the middle ground - not really 100% of the best from both worlds. Take a look at the code below utilizing ES6 syntax features like const, let, arrow functions and most notably - for... of loops and rest/spread operator:

const uselessFunction = (...numbers) => { let sum = 0; for(const number of numbers) { sum += number; } return sum; } uselessFunction(1,2,3,4); //10

That's looking pretty fine, to be honest. It's a not-really-useful function for returning sum of all passed parameters. Now, the way you would probably implement this in ES5 is:

function uselessFunction(numbers) { var sum = 0; for(var i = 0; i < numbers.length; i++) { sum += numbers[i]; } return sum; } uselessFunction([1,2,3,4]); //10

As you can see this is mostly the same code with the exceptions for a bit more code used for looping and the use of array instead of a list of parameters. It isn't really different from the example above. Considering you have updated your browser not that long ago and if you're not using IE (cause if you do then you must have some really important reasons behind this 🤔), then both of these snippets should work out-of-the-box when you execute them. Now, follow me in this particular scenario. You're considering yourself a modern JS developer and you do not start coding without Babel in place. And the fastest way to do this is just to install Babel and its default ENV preset without much hassle of configuring anything, and you start coding. When you finish writing your uselessFunction and you're reading to deploy it to production mode, something tickles you and you want to see the output of your modern code. And then... this happens:

"use strict"; var uselessFunction = function uselessFunction() { for (var _len = arguments.length, numbers = Array(_len), _key = 0; _key < _len; _key++) { numbers[_key] = arguments[_key]; } var sum = 0; for (var _iterator = numbers, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { var _ref; if (_isArray) { if (_i >= _iterator.length) break; _ref = _iterator[_i++]; } else { _i = _iterator.next(); if (_i.done) break; _ref = _i.value; } var number = _ref; sum += number; } return sum; }; uselessFunction(1, 2, 3, 4); //10

Now, let's end this terrible story right here and let's take a deeper look into what has exactly happened. Of course, the code above will be much, much slower than our ES5 hand-written alternative above. Not in the uselessFunction example really - it's too simple but most likely in more performance-demanding code. But, you can't really blame Babel for what has happened over here. It's just doing its job. You see, when using ENV preset without further configuration, you target IE 11 and Safari higher than 9 versions. This way Babel produces the output you can see above (not really the same as the ENV preset additionally adds error handling which isn't present here because I used ES2015-loose preset for a more fair comparison). This shows us how bad influence not properly configuring your compiler and not specifying lowest targeted versions of given browsers may have on the performance of your code.

Welcome to 2019!

So, by now it's been almost 4 years since the release of ECMAScript 6 / 2015 - happy new year BTW! 🎉 4 years is a really big amount of time especially looking at web development and technology in general. ES6 should be kind-of settled by now on most of the browsers and... it mostly is. But the habit of using compilers (sometimes without real need) has settled in minds of many developers. To help with this problem, here are some of the most popular ES6 features which have really great support in many browsers.

Const & let variable declarations

It may come as a surprise for you but const and let keywords have really good cross-browser support. Const is supported completely or partially in almost 95% of all web browsers - even IE 11! Let on the other hand comes right behind Const with around 90% of browsers supporting it and IE 11 having partial support. Sadly these features aren't the ones that make our compiled code so big - they can be easily polyfilled with the var keyword.

Arrow functions

Arrow functions are probably one of the most widely-used ES6 features. Providing a short form for writing functions - and especially callback ones - these also fix this context problem with lexical binding. With around 87% of global support, you can freely use these when not targeting IE 11 or Safari 9.x.

Promises

Promises provide a better way of dealing with asynchronous code without falling into callback hell. They come with 89% of support with an impressive achievement of being supported even in Safari 7.1 - sadly, no such luck for IE. Interestingly, async/await syntactic sugar over promises (part of ES2017 specs) is supported in 85% of all browsers. So, don't be scared to write async code in modern environments.

Rest parameters / spread operator

Rest parameters have fine support of 87% of global browsers. Actually, these are the main reason behind the big size of our Babel compiled code! 87% is not a bad score, but you can forget about supporting IE 11 or Safari 9.x without any polyfills.

Classes

Now, classes are a new, better way of dealing with the prototypical nature of JavaScript by utilizing syntactic sweets again. Support for 87% of used browsers including Safari 9.

So here you go with some examples or rather proofs that transpiler may not be really needed - at least in some cases. People are updating their browsers or even whole devices and as such the support will be only growing in a positive way. Keep in mind that we're talking only about clean JavaScript language here - no Web APIs or anything like that. If the number of examples above didn't really convince you - that's fine - at least I've tried. Raw data in percentage form is coming from CanIuse, so if you want - check it out!

Bottom line

The purpose of this whole post isn't to say that compilers, transpilers and etc. are bad - not at all! These are wonderful tools allowing you to write next-gen JavaScript today and, as JS is now in constant development, these are really useful! I'd rather want to point out at how important it is to know where your bottom line for backward support should be placed. Also, don't forget the code snippet above and take a closer look (at least sometimes) at the compiled code and your .babelrc file or whatever you're using.

Thanks for reading this post, if you liked it share it on Twitter or Facebook using buttons below and follow me for more development content. And again happy new year! ? Lastly, I'm really looking forward to ES2019!