RECAP:
Ok, it's been a while since I asked this question. As usual, I went and augmented the Object.prototype
anyway, in spite of all the valid arguments against it given both here and elsewhere on the web. I guess I'm just that kind of stubborn jerk.
I've tried to come up with a conclusive way of preventing the new method from mucking up any expected behaviour, which proved to be a very tough, but informative thing to do.
I've learned a great many things about JavaScript. Not in the least that I won't be trying anything as brash as messing with the native prototypes, (except for String.prototype.trim
for IE < 9).
In this particular case, I don't use any libs, so conflicts were not my main concern. But having dug a little deeper into possible mishaps when playing around with native prototypes, I'm not likely to try this code in combination with any lib.
By looking into this prototype approach, I've come to a better understanding of the model itself. I was treating prototypes as some form of flexible traditional abstract class, making me cling on to traditional OOP thinking. This viewpoint doesn't really do the prototype model justice. Douglas Crockford wrote about this pitfall, sadly the pink background kept me from reading the full article.
I've decided to update this question in the off chance people who read this are tempted to see for themselves. All I can say to that is: by all means, do. I hope you learn a couple of neat things, as I did, before deciding to abandon this rather silly idea. A simple function might work just as well, or better even, especially in this case. After all, the real beauty of it is, that by adding just 3 lines of code, you can use that very same function to augment specific objects' prototypes all the same.
I know I'm about to ask a question that has been around for quite a while, but: Why is Object.prototype considered to be off limits? It's there, and it can be augmented all the same, like any other prototype. Why, then, shouldn't you take advantage of this. To my mind, as long as you know what you're doing, there's no reason to steer clear of the Object prototype.
Take this method for example:
if (!Object.prototype.getProperties)
{
Object.prototype.getProperties = function(f)
{
"use strict";
var i,ret;
f = f || false;
ret = [];
for (i in this)
{
if (this.hasOwnProperty(i))
{
if (f === false && typeof this[i] === 'function')
{
continue;
}
ret.push(i);
}
}
return ret;
};
}
Basically, it's the same old for...in
loop you would either keep safe in a function, or write over and over again. I know it will be added to all objects, and since nearly every inheritance chain in JavaScript can be traced back to the Object.prototype
, but in my script, I consider it the lesser of two evils.
Perhaps, someone could do a better job at telling me where I'm wrong than this chap, among others.
Whilst looking for reasons people gave NOT to touch the Object
's prototype, one thing kept cropping up: it breaks the for..in
loop-thingy, but then again: many frameworks do, too, not to mention your own inheritance chains. It is therefore bad practice not to include a .hasOwnProperty
check when looping through an object's properties, to my mind.
I also found this rather interesting. Again: one comment is quite unequivocal: extending native prototypes is bad practice, but if the V8 people do it, who am I to say they're wrong?
I know, that argument doesn't quite stack up.
The point is: I can't really see a problem with the above code. I like it, use it a lot and so far, it hasn't let me down once. I'm even thinking of attaching a couple more functions to the Object prototype. Unless somebody can tell me why I shouldn't, that is.
The prototype is an object that is associated with every functions and objects by default in JavaScript, where function's prototype property is accessible and modifiable and object's prototype property (aka attribute) is not visible. Every function includes prototype object by default. Prototype in JavaScript.
Object.prototype.hasOwnProperty() The hasOwnProperty() method returns a boolean indicating whether the object has the specified property as its own property (as opposed to inheriting it).
Every JavaScript object has an internal "prototype" property, often called [[prototype]], which points to the object from which it directly inherits. This is exposed in FF and Chrome by the non-standard __proto__ property. Object.
Conceptually, all objects have a prototype (NOT A PROTOTYPE PROPERTY). Internally, JavaScript names an object's prototype as [[Prototype]]. There are two approaches to get any object (including non-function object)'s [[prototype]]: the Object. getPrototypeOf() method and the __proto__ property.
The fact is, it's fine as long as you know what you're doing and what the costs are. But it's a big "if". Some examples of the costs:
You'll need to do extensive testing with any library you choose to use with an environment that augments Object.prototype
, because the overwhelming convention is that a blank object will have no enumerable properties. By adding an enumerable property to Object.prototype
, you're making that convention false. E.g., this is quite common:
var obj = {"a": 1, "b": 2};
var name;
for (name in obj) {
console.log(name);
}
...with the overwhelming convention being that only "a" and "b" will show up, not "getProperties".
Anyone working on the code will have to be schooled in the fact that that convention (above) is not being followed.
You can mitigate the above by using Object.defineProperty
(and similar) if supported, but beware that even in 2014, browsers like IE8 that don't support it properly remain in significant use (though we can hope that will change quickly now that XP is officially EOL'd). That's because using Object.defineProperty
, you can add non-enumerable properties (ones that don't show up in for-in
loops) and so you'll have a lot less trouble (at that point, you're primarily worried about name conflicts) — but it only works on systems that correctly implement Object.defineProperty
(and a correct implementation cannot be "shimmed").
In your example, I wouldn't add getProperties
to Object.prototype
; I'd add it to Object
and accept the object as an argument, like ES5 does for getPrototypeOf
and similar.
Be aware that the Prototype library gets a lot of flak for extending Array.prototype
because of how that affects for..in
loops. And that's just Array
s (which you shouldn't use for..in
on anyway (unless you're using the hasOwnProperty
guard and quite probably String(Number(name)) === name
as well).
...if the V8 people do it, who am I to say they're wrong?
On V8, you can rely on Object.defineProperty
, because V8 is an entirely ES5-compliant engine.
Note that even when the properties are non-enumerable, there are issues. Years ago, Prototype (indirectly) defined a filter
function on Array.prototype
. And it does what you'd expect: Calls an iterator function and creates a new array based on elements the function chooses. Then ECMAScript5 came along and defined Array.prototype.filter
to do much the same thing. But there's the rub: Much the same thing. In particular, the signature of the iterator functions that get called is different (ECMAScript5 includes an argument that Prototype didn't). It could have been much worse than that (and I suspect — but cannot prove — that TC39 were aware of Prototype and intentionally avoided too much conflict with it).
So: If you're going to do it, be aware of the risks and costs. The ugly, edge-case bugs you can run into as a result of trying to use off-the-shelf libraries could really cost you time...
If frameworks and libraries generally did what you are proposing, it would very soon happen that two different frameworks would define two different functionalities as the same method of Object
(or Array
, Number
... or any of the existing object prototypes). It is therefore better to add such new functionality into its own namespace.
For example... imagine, you would have a library that would serialize objects to json and a library that would serialize them to XML and both would define their functionality as
Object.prototype.serialize = function() { ... }
and you would only be able to use the one that was defined later. So it is better if they don't do this, but instead
JSONSerializingLibrary.seralize = function(obj) { ... }
XMLSerializingLibrary.seralize = function(obj) { ... }
It could also happen that a new functionality is defined in a new ECMAscript standard, or added by a browser vendor. So imagine that your browsers would also add a serialize
function. That would again cause conflict with libraries that defined the same function. Even if the libraries' functionality was the same as that which is built in to the browser, the interpreted script functions would override the native function which would, in fact, be faster.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With