My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Friday, June 03, 2011

Partial Polyfills

This is a quick one already discussed during my recent workshop in Warsaw, a concept rarely considered or adopted from JS developers.

What Are Polyfills

If we are updated enough to know ECMAScript 5th Edition, we probably know all possible shims and fallbacks for Array extras as well (e.g. Array.prototype.forEach).
Polyfills are simply snippets able to bring features already available for most recent browsers into those not updated yet.

Why Polyfills

If we develop with present and future in mind, we can take advantage of most recent techniques and solutions in order to both speed up via native support, where available, and maintain our application just stripping out code once all target browsers support them natively ... isn't this cool ?!

Polyfills Side Effects

The most common side effect of a polyfill is performances impact. The Array::forEach is already a good example of decreased performances.
If we think about a forEach, it's nothing different than a classic for loop, except we can reuse a function and eventually inject a different context.
As summary, while the code size will be reduced and all "Array loops gotcha" resolved nicely, old browsers already slower will be even slower due simulated native behavior implemented through JavaScript.
The second most important side effect is that sometimes we cannot possibly reproduce the new native behavior via old JavaScript, which means we are compromising logic and potentials of our application in order to support "not there yet" browsers.

Partial Polyfills

Once we have defined a "full specs simulated" polyfill we can forget problems and use it whenever we want. Unfortunately, we have to agree with all other external libraries as well, libraries that would like to be dependencies free and that most likely will re-implement or re-check over and over if that polyfill is present or not.
On the other hand, there are many situations were we do not need the full implementation of a missed feature and in these cases the advantage of a partial polyfill will result in reduced and safer code.

What Is A Partial Polyfill

Let's take the classic Array.prototype.indexOf as example and check the size and the "complexity" of the full spec proposal in MDC ... done?

// our main library closure
(function () {

// all we may need to indexOf
var indexOf = [].indexOf || function (value) {
for (var i = this.length; i-- && this[i] !== value;);
return i;
};

// wherever we need ...
var index = indexOf.call(arrayLikeObject, value);

// end of our main closure
}());

If we deal with properly defined arrays, arguments, or DOM collections, and if we use indexOf simply to avoid duplicated entries in a generic stack, ask yourself if it makes sense to implement the full specs there rather than those two lines of JS showed above.
As somebody noticed, that snippet is more similar to Array.prototype.lastIndexOf, due reversed loop, but why bother if our indexOf use case is specific?
Why pollute the global Array.prototype causing potential problems for other libraries?
Why make the "search into array" routine more expensive for already slower browsers?
In few words we can reuse few extra bytes in every closure we want to obtain a simplified indexOf behavior compatible with every browser, isn't it ?!

Why Safer

Unless we are not sure 100% that the Array.prototype.indexOf has been replaced with a full specs version and since we may lazy load extra code that may be greedy or change again that prototype, we can address that callback and use call as much as we want being sure that no library will ever change our expected performances and/or behavior.

Why Smaller

Not only we can copy and paste those two lines of code in many places without impacting code size, we can also take advantage of most popular minifiers, able to make each call smaller.

// minified indexOf through prototype
a.indexOf(b);

// minified addressed indexOf
x.call(a,b);

Last, but not least, using call we can automatically use by default the indexOf with objects and every other ArrayLike variable.

Conclusion

There are many cases where we may need one or more ES5 feature but not all of them will be used 100% of their potentials. A partial polyfill can ensure better performances and easier access in the private scope.
Surely we may avoid this technique if by default we normalize all behaviors but once again, we all know we don't like much core functionalities dependencies that may be broken or extremely slow for what we need in our specific cases.

No comments: