After 13 Years of Polyfills

Andrea Giammarchi
Jul 27 · 8 min read

Whenever you are complaining about current JS fragmentation, remember that in the 18th of July 2006 I’ve published my first JavaScript Standard Library polyfill, targeting browsers down to IE4, a browser that never implemented try/catch statement, a browser super hard to feature detect.

Most Array.prototype methods where not available in IE, while XMLHttpRequest was not available in Firefox and others.

The String.prototype.replace was not accepting a function as second argument, and yet most documentation was still provided by Mozilla ♥

Dropping IE4 and publishing V2

At the end of 2007, I’ve finally dropped support for IE4 with an ECMAScript 4th Edition based revision of my library: JSL V2.

At that time there were browsers not supporting undefined, and both setTimeout and setInterval where supporting extra arguments everywhere but in, guess who, IE.

This time in Web history was known as Web 2.0, a time where IE5 was still around and things we give for granted such as Function.prototype.call and Function.prototype.apply where not implemented properly, as well as Object.prototype.hasOwnProperty.

IE was still dominant

Despite MDN effort to document standards, and while developers were all on Firefox thanks to Firebug developers tool, the majority of websites were still targeting IE which had most relevant market share, and admittedly quite some interesting, obtrusive or security unfriendly, features too.

This situation was reflected on the market in a way that many developers were aware of IE specific APIs, but incapable of writing standard code.

Despite the usage of obvious libraries such as jQuery, those die-hard “vanilla JS only” developers were left behind an enormous amount of inconsistencies.

The subclassing case

For various years, few JS enthusiast tried to subclass native functions, in an era where Object.setPrototypeOf and class {} were nowhere in specs.

Already in 2006, Dean Edwards managed to subclass an Array, in a fairly limited way, and since that day many other developers tried to subclass most used classes in a way that worked similarly to modern native subclassing.

During 2008 I’ve managed to go beyond Dean’s implementation limits, but also subclassed later on the String in two different occasions.

As of today, people targeting ES5 browsers still have tons of issues with subclasses, so that even if this topic is as old as the Web (or half of its time), my hint is that you should probably not subclass anything if you transpile ES2015 native classes, something also not needed anymore, unless you target death browsers such as IE11 or below.

This is valid for TypeScript too: classes are broken there if you target ES5.

The vice-versa project

Around April 2009, I had the idea of polyfilling browsers both ways.

The project is still hosted in the archaic Google Code space, ’cause in those days GitHub didn’t exist, so that TortoiseSVN was “the tool used by pros”.

In those days, IE6 was still around, and hard-core devs named themselves ninjas, mostly due jQuery and its’ John author’s book, to which I’ve contributed too, at least for the DOMContentLoaded hack part.

A quick recap of Web developers nick names to date

  • ninja
  • Jedi
  • rock-star
  • full-stack
  • vanilla

I am not sure how we went from ninja to vanilla, but I hope the next round of nick names would sound like cocktails, as in “the bloody-mary of CSS” or “the JD on the rocks of utilities” … uh wait, he exists already 🤔 …

… but no getters or setters

Until I’ve started hacking with VBScript on IE, the only programming language that was competing with JavaScript on the Web and that lost pretty much every single battle, every library until 2011 could not implement some magic through accessors, at that time also standardized for IE9 and above, in a world still dominated by IE6, IE7, and IE8 with its broken ES5 implementation.

VBScript Classes were the source of my hack to bring getters and setters everywhere, but I was a bit late to the “let’s use this hack in cool libraries” party, so that such hack never got adopted much.

The hack had some limitation, like the one that virtually private fields could not start with an _ underscore, but they could have an underscore at the end, because VBScript was that stupid, yes!

… and a lot of XPath …

In those years CSS selectors were not as powerful as today, and XPath was the way to go for anything more complex than IDs or classes.

There are still things XPath does better than CSS selectors, as example by being able to crawl the parent at any point, but I believe modern developers forgot about XPath or stopped using it something like 5 years ago or more.

From monolith to partial

In 2011 I’ve been super active as a Speaker, covering conferences such as JSConf EU and falsy-values in Warsaw.

In these days I was using daily ES5, polyfilling for this or that Desktop or Mobile browser what was needed, and never more.

Around that time thouhg, or not much after, the full ES5 shim monolith became one of the most popular scripts to put on top of any Web page.

I’ve both contributed to, but practically never used, such project, simply because optimizing the overall JS size of any site was still my most important daily task, and simply because I was developing for Mobile Web, where 3/4th of the code present in that monolith was useless: we didn’t care about IE 🎉

The unpolyfillable case

When ECMAScript proposed WeakMap and WeakSet, we’ve faced, for the first time, something truly impossible to 100% polyfill, something that no library could’ve reliably fixed, even through a specialized API.

Acknowledging the huge amount of edge-cases in which a WeakMap polyfill would’ve failed, I’ve published a library that was simply leaking all objects forever. As of today, that’s probably my most epic-fail approach to a polyfill, but luckily other developers used smarter approaches, approaches only possible with monoliths though, because of the controversial nature of the fixes needed, and used, to polyfill these new classes.

On a similar note, Symbol came out and that’s where transpilers could be the only option to obtain typeof thing === "symbol" because every other runtime fix could’ve not being able to address that.

Delivered under the es-shims umbrella, my Symbols polyfill started being used anyway, despite the few limitations, at least well documented.

The broken polyfill case

For a monolith polyfill, it’s easy to play well with its inner cases. However, when you have other libraries, or patches, in place, and the polyfill breaks your code, things become easily not nice at all.

As of today, if I need to patch after feature detection some browser quirk, I also need to be sure my patch won’t affect, or break, other polyfills.

This is not as simple and straight forward as it sounds, ’cause you need to know internals of every other major library/polyfill that might break, or that will break your code, and today there are still various issues here and there with concurrent polyfills, due execution order, or obtrusiveness of the code.

The Babel era

With the introduction of many new features, toward the ES2015 release, Babel was the best, if not the only, tool to keep up to date your JS code, without worrying about browsers incompatibilities.

The project has been based on core-js pretty much since ever, which is an incremental polyfill able to provide helpers on the fly, accordingly to the code you transpile.

This has been both revolutionary and misleading for these reasons:

  • as soon as you target an older browser, all modern browsers are affected by the potentially polyfilled code too
  • it’s super easy to have undesired bloat if you don’t keep an eye on the produced code, something I believe mostly nobody does

Regardless, Babel is today still the best transpiler you can bet on, and its internal pollyfills are also super tested thanks to its community’s adoption.

I’ve recently, and unsurprisingly, contributed to its native class subclassing transpilation, but I guess by now you could’ve seen that coming 😅

Did Babel kill all polyfills ?

Not quite yet. There are hundreds projects not using Babel, or shipping code that should resist to Babel transpilations (i.e. libraries that can be imported).

For all these cases, there are at least three alternatives:

  • use monoliths on top of every Web page
  • use a CDN that polyfills browsers after user agent sniffing
  • use partial polyfills only when needed

While the first approach is still dominant in old style websites, but I wouldn’t suggest such approach to any 2019+ site, the CDN based polyfill.io case is super interesting, surpassed only by the recent Pika CDN project, able to embed polyfills only if the used code needs ’em, similarly to what Babel does in core, except it abstracts away all the tooling burden needed to serve this or that browser.

But where could you find modern partial polyfills that solve both JS and DOM cases?

Not only JavaScript

While most monoliths mentioned in this post have historically polyfilled ECMAScript features, I’ve kept polyfilling since vice-versa project all the common gaps between browsers, when it comes to DOM APIs.

The Custom Elements V0/V1 polyfill, and dom4 before it, or even IE8, are just few examples of the various projects I’ve kept working on, to bring modern standards to older browsers.

These days though, I’ve decided to focus in one single place all my most relevant fixes, to simplify even to myself the research to fix modern gaps.

The ungap project

As anticipated, if you still need partial polyfills, instead of monoliths, and you can’t control where your code will end up being executed, your “one stop to rule them all” project is ungap.

Photo by Denny Luan on Unsplash

This is a collaborative project not even under my name, with very little rules but great code quality and cross browser compatibility.

Together with best practices to incrementally serve polyfills on demand, the best part of @ungap namespace is that it easily becomes vaporware.

Not only the Pika CDN won’t serve extra code when ungap modules are imported, the @ungap/degap project can instrument Rollup to completely skip/ignore specific modules.

On top of that, there is a babel-plugin-remove-ungap plugin that can similarly target modern browsers, avoiding any polyfill bloat.

Moving the Web forward

I’m some sort of JS purist, when it comes to development, so that transpilers, with their indirection, aren’t usually something I use or trust much.

However, I recognize Babel as the best tool ever to keep moving forward with the least amount of side effects.

Regardless, this whole post point was to both tell a bit of the Web polyfills history, and to underline that people involved in polyfillng the Web, and all its modern features, have been around for quite a while.

Accordingly, avoiding polyfills, as I’ve recently read in some twitter discussion, shouldn’t be a trend, ’cause it means you are avoiding the native functionalities of tomorrow, keeping your code base 10 years older than it should.

After all, like it’s already for the whole ungap namespace, polyfills will be your tomorrow’s vaporware, making your code faster, reliable, and slimmer, as years come.

Last, but not least, these have been 13 years of polyfilling for me, but I’m pretty sure polyfills author will stick around until the end of the Web time … well, unless, of course, both standards and browsers will be driven by the same team, or robot, but honestly, this Sci-fi ending wasn’t my intent 😜

Andrea Giammarchi

Written by

Web, Mobile, IoT, and all JS things since 00's. Formerly JS engineer at @nokia, @facebook, @twitter.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade