Javascript – What’s wrong about extending a class with prototype methods

functionsjavascriptprototyping

I was at a bar last night with a few of my colleagues. They said that it's a bad idea to extend the functionality of basic JavaScript Objects with a prototype method.

For example, let's say you created a method for finding the factorial

Number.prototype.factorial = function(n) {
  return n == 0 ? 1 : factorial(n - 1)

}

They said there was some danger to creating prototypes. Why would this be a bad practice?

Best Answer

The problem is the global scope. In other words, every change you make affects the whole code base, and yours may in turn be affected in any location of the code base.

Imagine that you want to loop through all elements of an array; imagine that browsers don't support it yet. For that, you create a method forEach which is called this way:

[5, 7, 1, 1, 3].forEach(function (element) {
    // Do something here.
});

You implemented this method, thought about some edge cases (what about an empty array, for example?) and everything is fine, except a slight performance bug you missed: in some cases, the enumeration is very slow.

Now, somewhere else, your colleague had this great idea of having a forEach method, but doesn't know you have already implemented one; actually, he even tried to call forEach on an array to ensure the method doesn't exist yet, but since your code is not executed on every page, [].forEach returns undefined.

So he creates his own implementation, but forgets to test it for an empty array, and, in fact, when the array is empty, his code fails.

Back to your code, one day you find a bug report telling that there is a error around forEach: when the array is empty, it fails. You don't know that your colleague had his own implementation of forEach which overwrites yours on some pages. Therefore, you think that it's your code which is concerned, so you spend hours wondering why is it failing, while your unit tests pass.

You end up finding the culprit, that is the duplicated forEach, and you decide to remove the code of your colleague—yours is better. The next day, a new bug report tells that [].forEach returns undefined on some pages—that is pages where your code is not executed. WTF!

Together with your colleague, you spend additional two hours resolving this issue. Finally, you are now sure that your code is executed on every page, and you have a clean code where the prototype is added only if there is no forEach method already:

if (![].forEach) {
    Array.prototype.forEach = function (...) {
        ...
    };
}

Meanwhile, the small performance bug you had in your code is discovered, but it appears that it is more a feature, and several of your colleagues are relying on this bug: in several locations, they absolutely need the enumeration to be slow in order to have enough time to show a few images. Since rewriting their code seems to be a nightmare, you decide to keep the bug.

A year later, one morning, you discover dozens of bug reports from dozens of customers. Something is going on. Somebody on your team discovers that all bug reports are from users of Chrome, and further analysis finds that the JavaScript engine of the newly released version of Chrome has forEach, with the difference that it doesn't have your bug/feature your colleagues were relying on. Now, your team will spend two days adapting the code to the new, bug-less implementation of forEach.

Related Topic