I'm looking for a definitive answer to why extending built-in prototypes is so heavily chastised in the JS developer community. I've been using the Prototype JS framework for a while, and to me doing [1,2,3].each(doStuff)
seems much more elegant than $.each([1,2,3], doStuff)
. I know that it creates "namespace pollution," but I stil don't understand why it's considered to be a bad thing. Also is there any real performance degradation associated with extending built-in prototypes? Thanks!
5 Answers
I suggest you to read this article which I think explains pretty well why extending objects is a bad idea, with regard to Prototype also.
In summary:
Lack of specification
Exposure of "prototype objects" is not part of any specification. [...] In order for implementation to conform to DOM Level 2 fully, there's no need to expose those global Node, Element, HTMLElement, etc. objects.
Host objects have no rules
DOM objects are host objects [...] Host objects may implement these internal methods with any implementation-dependent behaviour, or it may be that a host object implements only some internal methods and not others.
[...] Internal methods behavior is implementation-dependent. [...] By definition, you are working with something that's allowed to behave in unpredictable and completely erratic way.
Chance of collisions
Given huge amount of environments in use today, it becomes impossible to tell if certain property is not already part of some DOM. [...]
Every named form control shadows properties inherited through prototype chain. The chance of collisions and unexpected errors on form elements is even higher.
Employing some kind of prefixing strategy can alleviate the problem. But will probably also bring extra noise.
Performance overhead
[...] browsers that don’t support element extensions—like IE 6, 7, Safari 2.x, etc.—require manual object extension. The problem is that manual extension is slow, inconvenient and doesn’t scale.
[...] once you start extending elements, library API most likely needs to return extended elements everywhere. As a result, querying methods like $$ could end up extending every single element in a query.
IE DOM is a mess
As shown in previous section, manual DOM extension is a mess. But manual DOM extension in IE is even worse [...]
Bonus: browser bugs
Another reason is code readability/maintainability. If another developer (especially a newbie) is reading my code and sees [0, 1, 2].foo(...)
, they may not know what the foo method is or where to find documentation/source for it. Is foo is an extension to the language added by prototype.js, or by another library in use, or by some other portion of my code in another file, or is it a native JavaScript method that they didn't know about? They need to hunt for it and may not find it right away (or if there are conflicts they may not find the right one).
With the jQuery approach, if you see $.foo(...)
, the namespace of the foo method makes it obvious where to find its definition/documentation if you don't know what it does.
-
Discoverability of where methods come from is very important for readers. Although I don't really think jQuery is a good example as the dollar-sign is search challenging when you jump into reading web code and don't already know what it is. Jan 30, 2017 at 1:18
Here's the basic issue: What happens if you have two tools that extend prototypes in incompatible ways, or which extend commonly-called methods in ways such that they yield different results (this is a particular issue for for...in
in JavaScript), thus causing code that relies on their normal behavior to break?
Basically, it's the same issues that you have when you mis-use global variables. By itself, perhaps nothing bad happens. But, it opens you up for trouble when two ostensibly-separate pieces of code suddenly step on each other (and it's a pain to debug when that happens).
Certainly prototype.js is pretty well-known and most tools work around what it does. Similarly, I'm sure there are cases where extending base prototypes is the right thing to do. But, it's something to approach with caution.
Not sure if this is really still an issue anymore, but my experience with earlier versions of Internet Explorer is that sometimes it wasn't even possible to extend certain build-in types.
There are two separate issues here. The first is the general extending of built-in prototypes, and the other is specifically extending DOM prototypes. The arguments against extending built-in prototypes:
- Potential clashes: two pieces of code from different sources both defining the same property on the same prototype
- Side-effects: extending
Array.prototype
orObject.prototype
can have knock-on effects, such as adding the extension methods being enumerated in afor...in
loop
As for extending DOM prototypes, the potential clash argument above still applies. Additionally, DOM nodes are host objects and as such are not subject to any of the normal rules of native JavaScript objects. They can essentially do what they like and are under no obligation to provide sensible prototype objects or even allow extra ("expando") properties. IE in particular exercises this right, providing no prototypes for DOM objects before IE 9 and having various weirdnesses about properties on various DOM objects (although you're generally OK assigning properties to elements, provided nothing's set document.expando
to false
.)
for(var ... in ...)
loops get messed up since prototype functions are passed too.