4

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?

user439133
  • 137
  • 1
  • 4
  • related: [Are there any OO-principles that are practically applicable for Javascript?](http://programmers.stackexchange.com/a/180588/31260) – gnat Jun 25 '15 at 13:15

2 Answers2

14

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.

Arseni Mourzenko
  • 134,780
  • 31
  • 343
  • 513
1

Abhi explained on Stack Overflow why you don't want to do it to common objects, but for your own class, that's the way of doing it. So in short, the answer is DEPENDS.

When you extend an object, you change its behaviour.

Changing the behaviour of an object that will only be used by your own code is fine. But when you change the behaviour of something that is also used by other code there is a risk you will break that other code.

When it comes adding methods to the object and array classes in javascript, the risk of breaking something is very high, due to how javascript works. Long years of experience have taught me that this kind of stuff causes all kinds of terrible bugs in javascript.

If you need custom behaviour, it is far better to define your own class (perhaps a subclass) instead of changing a native one. That way you will not break anything at all.

The ability to change how a class works without subclassing it is an important feature of any good programming language, but it is one that must be used rarely and with caution.

Also, there is a performance aspect of this as well.

Let's say you added a property to a prototype of some class. The way JS works, is when you try to access a property on any object, it will try to find it on this object, if not found, JS will proceed to check it's prototype, and prototype of the prototype.. and so on, until it hits the very top where no more prototype is found. If you add the property to immediate prototype of your class, you are essentially producing more efficient code than adding to prototype of a common object in JS.

But, again, what this means is - use this carefully with understanding what it does. Anything is bad when used inappropriately.

Alexus
  • 2,368
  • 15
  • 24
  • 2
    `If you add the property to immediate prototype of your class, you are essentially producing more efficient code than adding to prototype of a common object in JS` -- The difference in efficiency is almost certainly insignificant. Other factors are far more important, like having the ability to provide [useful helper methods on already existing prototypes](https://github.com/pcurry/crockford-libs/blob/master/cf-lib.js). – Robert Harvey Jun 25 '15 at 17:42
  • 1
    Also, I disagree with Abhi's stance that it is not safe to add methods to existing objects or prototypes. The Javascript language is designed specifically to do this and, unlike the way `==` works, it's actually one of the "good parts." See [this post](http://engineering.wix.com/2015/04/21/javascript-the-extra-good-parts/) for some very useful prototype enhancements from Douglas Crockford. – Robert Harvey Jun 25 '15 at 17:44
  • 1
    I don't argue that this is a feature of the language, and certainly one of the good ones. However, I do believe that it can pose danger when used inappropriately. – Alexus Jun 25 '15 at 17:51
  • 2
    @RobertHarvey's link is broken. [Here](https://www.wix.engineering/post/javascript-the-extra-good-parts) is a working one – Gust van de Wal Nov 26 '19 at 15:23