Extending Object.prototype JavaScript - javascript

I am not asking if this is okay:
Object.prototype.method = function(){};
This is deemed evil by pretty much everyone, considering it messes up for(var i in obj).
The Real Question
Ignoring
Incompetent browsers(browsers that don't support Object.defineProperty)
Potential for property collision or overriding
Assuming you have some incredibly useful method, is this considered wrong/unethical?
Object.defineProperty(Object.prototype, 'methodOnSteriods',{
value: function(){ /* Makes breakfast, solves world peace, takes out trash */ },
writable: true,
configurable: true,
enumerable: false
});
If you believe the above is unethical, why would they even implement the feature in the first place?

UPDATE from 2021
Despite this being the accepted answer, 10 years of experience has taught me this isn't the best idea. Pretty much anything you can do to avoid polluting the global scope is a very very good thing.
Original answer below, for posterity, and because stack overflow will not let me delete an accepted answer.
Original answer from 2011
I think it's fine if it works in your target environment.
Also I think prototype extension paranoia is overblown. As long as you use hasOwnProperty() like a good developer that it's all fine. Worst case, you overload that property elsewhere and lose the method. But that's your own fault if you do that.

I'd say this is almost as evil as before. The biggest problem, still the same as before, is that Object.prototype is global. While your method might currently be solving world peace, it might have overwriten someone else's method (that was guaranteeing galactic peace) or may be overwritten in the future by some library you have no control over (therefore plunging the world into chaos again)
New versions of Javascript have lots of features related to properties, such as definig a property to be enumerable/not enumerable, having getters and setters... Object.defineProperty existis to give control over this.
From Mozilla Docs:
This method allows precise addition to or modification of a property
on an object. Normal property addition through assignment creates
properties which show up during property enumeration (for...in loop),
whose values may be changed, and which may be deleted. This method
allows these extra details to be changed from their defaults.
This new function is basically required to support the new features and you are supposed to use it on your own stuff. Being able to modify Object.prototype is just a side effect of it also being a "normal" object and is just as evil as before.

Well in "JavaScript: the good parts", there is a similar function, i think that is very usefull to improve javascript base objects (like String, Date, etc..), but just for that.
// Add a method conditionally. from "JavaScript: the good parts"
Function.prototype.method = function (name, func) {
if (!this.prototype[name]) {
this.prototype[name] = func;
}
}

.hasOwnProperty() will exclude iteration through inherited properties, which I personally find is often more annoying than helpful. It largely defeats the usefulness of Object.create()—which is ironic since the same guy who convinced everyone to do .hasOwnProperty() also promoted Object.create().
Object.prototype should not be extended, for the reasons listed here. If you really do want to extend it, then make the extensions non-iterable.
I realize this flies in the face of all of the published best-practices, but we really should stop “mandating” .hasOwnProperty() on object key iterations and embrace the usefulness of direct object-to-object inheritance.

The short answer is Yes, you should do it.
Before doing it, there are several precautions need to take:
1. using hasOwnProperty when iterating object, but this isn't really a precautions, when iterating object, I am already using hasOwnProperty anyway.
2. check if the name in Object.prototype.name has existed, this is very safe to avoid name collision.
3. take advantage of Object.defineProperty(), just add extra safeguard layer.
As you can see, it's not very complicated.
Now comes the advantages once you have taken care of the risks/disadvantages:
1. method chaining, this just makes code more readable, terse, and making coding more enjoyable. In turn makes you happier, and your life easier.
2. solves the browser compatibility issue, you are doing polyfill anyway.
P.S.:
Don't do it when working with a large team for sure.

Related

JavaScript: Extending Element prototype

I have seen a lot of discussion regarding extending Element. As far as I can tell, these are the main issues:
It may conflict with other libraries,
It adds undocumented features to DOM routines,
It doesn’t work with legacy IE, and
It may conflict with future changes.
Given a project which references no other libraries, documents changes, and doesn’t give a damn for historical browsers:
Is there any technical reason not to extend the Element prototype. Here is an example of how this is useful:
Element.prototype.toggleAttribute=function(attribute,value) {
if(value===undefined) value=true;
if(this.hasAttribute(attribute)) this.removeAttribute(attribute);
else this.addAttribute(attribute,value);
};
I’ve seen too many comments about the evils of extending prototypes without offering a reasonable explanation.
Note 1: The above example is possibly too obvious, as toggleAttribute is the sort of method which might be added in the future. For discussion, imagine that it’s called manngoToggleAttribute.
Note 2: I have removed a test for whether the method already exists. Even if such a method already exists, it is more predictable to override it. In any case, I am assuming that the point here is that the method has not yet been defined, let alone implemented. That is the point here.
Note 3: I see that there is now a standard method called toggleAttribute which doesn’t behave exactly the same. With modification, the above would be a simple polyfill. This doesn’t change the point of the question.
Is it ok? Technically yes. Should you extend native APIs? As a rule of thumb no. Unfortunately the answer is more complex. If you are writing a large framework like Ember or Angular it may be a good idea to do so because your consumers will have Benifits if better API convenience. But if you're only doing this for yourself then the rule of thumb is no.
The reasoning is that doing so destabilizes the trust of that object. In other words by adding, changing, modifying a native object it no longer follows the well understood and documented behavior that anyone else (including your future self) will expect.
This style hides implementation that can go unnoticed. What is this new method?, Is it a secret browser thing?, what does it do?, Found a bug do I report this to Google or Microsoft now?. A bit exaggerated but the point is that the truth of an API has now changed and it is unexpected in this one off case. It makes maintainability need extra thought and understanding that would not be so if you just used your own function or wrapper object. It also makes changes harder.
Relevant post: Extending builtin natives. Evil or not?
Instead of trying to muck someone else's (or standard) code just use your own.
function toggleAttribute(el, attribute, value) {
var _value = (value == null ? true : value;
if (el.hasAttribute(attribute)) {
el.removeAttribute(attribute);
} else {
el.addAttribute(attribute, _value);
}
};
Now it is safe, composible, portable, and maintainable. Plus other developers (including your future self) won't scratch their heads confused where this magical method that is not documented in any standard or JS API came from.
Do not modify objects you don't own.
Imagine a future standard defines Element.prototype.toggleAttribute. Your code checks if it has a truthy value before assigning your function. So you could end up with the future native function, which may behave differently than what you expected.
Even more, just reading Element.prototype.toggleAttribute might call a getter, which could run some code with undesired sideways effects. For example, see what happens when you get Element.prototype.id.
You could skip the check and assign your function directly. But that could run a setter, with some undesired sideways effects, and your function wouldn't be assigned as the property.
You could use a property definition instead of a property assignment. That should be safer... unless Element.prototype has some special [[DefineOwnProperty]] internal method (e.g. is a proxy).
It might fail in lots of ways. Don't do this.
In my assessment: no
Massive overwriting Element.prototype slow down performance and can conflict with standardization, but a technical reason does not exist.
I'm using several Element.prototype custom methods.
so far so good until I observe a weird behaviour.
<!DOCTYPE html >
<html >
<body>
<script>
function doThis( ){
alert('window');
}
HTMLElement.prototype.doThis = function( ){
alert('HTMLElement.prototype');
}
</script>
<button onclick="doThis( )" >Do this</button>
</body>
</html>
when button is clicked, the prototype method is executed instead of the global one.
The browser seems to assume this.doThis() which is weird. To overcome, I have to use window.doThis() in the onclick.
It might be better if w3c can come with with diff syntax for calling native/custom methods e.g.
myElem.toggleAttribute() // call native method
myElem->toggleAttribute() // call custom method
Is there any technical reason not to extend the Element prototype.
Absolutely none!
pardon me:
ABSOLUTELY NONE!
In addition
the .__proto__, was practically an a illegal (Mozilla) prototype extension until yesterday. - Today, it's a Standard.
p.s.: You should avoid the use of if(!Element.prototype.toggleAttribute) syntax by any means, the if("toggleAttribute" in Element.prototype) will do.

Revisiting extending native prototypes after ECMAScript 5

Recently, given the changes to defining properties in ECMAScript 5, I have revisited the question of whether we can safely extend the native JavaScript prototypes. In truth, all along I have extended prototypes like Array and Function, but I avoided doing so with Object, for the obvious reasons. In unit testing with Jasmine, by adding Object.prototype specs to specs for my own personal framework, extending Object.prototype with non-enumerable functions has appeared to be safe. Data properties like a "type" property, however, with getters/setters that do any unusual processing have had unintended consequences. There is still the possibility of conflicts with other libraries--though in my work, that hardly ever comes up. Nevertheless, as long as the functions are not enumerable, it looks like extending Object.prototype can be safe.
What do you think? Is it safe to extend Object.prototype now? Please discuss.
Extending objects native to JavaScript might become a little safer, through many collision concerns still stand. Generally, unless you're extending object to support standartized behavior from more recent standard it really would be still much safer to introduce wrapper - it is much easier to do things right way when you're the only one in control.
Speaking of objects native to environment (DOM elements and nodes, AJAX stuff), new JS standard still don't give and, arguably, can't give you any guarantee about any interaction with those except what defined in their interface standard. Never forget that they're potentially accessible through many different scripting engines and thus not need to be tailored for quirks of one specific language - JS. So recommendation to not extend those either still stands as well.
The definitive, absolute answer is ...
"It depends." :)
Extending any built in JavaScript object can be perfectly safe or it can be a complete disaster. It depends on what you are doing and how you are doing it.
Use smart practices and common sense and test the hell-out-of-it.

Advantage of using Object.create

Similar to, but different from this question. The code below is from JavaScript: The Definitive Guide. He's basically defining an inherit method that defers to Object.create if it exists, otherwise doing plain old Javascript inheritance using constructors and swapping prototypes around.
My question is, since Object.create doesn't exist on plenty of common browsers IE, what's the point of even trying to use it? It certainly clutters up the code, and one of the commenters on the previous question mentioned that Object.create isn't too fast.
So what's the advantage in trying to add extra code in order to occasionally utilize this ECMA 5 function that may or may not be slower than the "old" way of doing this?
function inherit(p) {
if (Object.create) // If Object.create() is defined...
return Object.create(p); // then just use it.
function f() {}; // Define a dummy constructor function.
f.prototype = p; // Set its prototype property to p.
return new f(); // Use f() to create an "heir" of p.
}
The speed difference is not very noticeable, since by nature you probably won't be creating too many objects (hundreds, even thousands isn't what I call a lot), and if you are and speed is a critical issue you probably won't be coding in JS, and if both of the above aren't true, then I'm sure within a few releases of all popular JS engines the difference will be negligible (this is already the case in some).
In answer to your question, the reasons aren't speed-related, but because the design pattern of Object.create is favoured to the old method (for the reasons outlined in that and other answers). They allow for proper utilisation of the ES5 property attributes (which make for more scalable objects, and thus more scalable apps), and can help with inheritance hierarchies.
It's forward engineering. If we took the line of "well it isn't implemented everywhere so let's not get our feet wet", things would move very slowly. On the contrary, early and ambitious adoption helps the industry move forward, helps business decision makers support new technologies, helps developers improve and perfect new ideas and the supporting frameworks. I'm an advocate for early (but precautionary and still backward-compatible) adoption, because experience shows that waiting for enough people to support a technology can leave you waiting far too long. May IE6 be a lesson to those who think otherwise.

Why is it frowned upon to modify JavaScript object's prototypes?

I've come across a few comments here and there about how it's frowned upon to modify a JavaScript object's prototype? I personally don't see how it could be a problem. For instance extending the Array object to have map and include methods or to create more robust Date methods?
The problem is that prototype can be modified in several places. For example one library will add map method to Array's prototype and your own code will add the same but with another purpose. So one implementation will be broken.
Mostly because of namespace collisions. I know the Prototype framework has had many problems with keeping their names different from the ones included natively.
There are two major methods of providing utilities to people..
Prototyping
Adding a function to an Object's prototype. MooTools and Prototype do this.
Advantages:
Super easy access.
Disadvantages:
Can use a lot of system memory. While modern browsers just fetch an instance of the property from the constructor, some older browsers store a separate instance of each property for each instance of the constructor.
Not necessarily always available.
What I mean by "not available" is this:
Imagine you have a NodeList from document.getElementsByTagName and you want to iterate through them. You can't do..
document.getElementsByTagName('p').map(function () { ... });
..because it's a NodeList, not an Array. The above will give you an error something like: Uncaught TypeError: [object NodeList] doesn't have method 'map'.
I should note that there are very simple ways to convert NodeList's and other Array-like
Objects into real arrays.
Collecting
Creating a brand new global variable and stock piling utilities on it. jQuery and Dojo do this.
Advantages:
Always there.
Low memory usage.
Disadvantages:
Not placed quite as nicely.
Can feel awkward to use at times.
With this method you still couldn't do..
document.getElementsByTagName('p').map(function () { ... });
..but you could do..
jQuery.map(document.getElementsByTagName('p'), function () { ... });
..but as pointed out by Matt, in usual use, you would do the above with..
jQuery('p').map(function () { ... });
Which is better?
Ultimately, it's up to you. If you're OK with the risk of being overwritten/overwriting, then I would highly recommend prototyping. It's the style I prefer and I feel that the risks are worth the results. If you're not as sure about it as me, then collecting is a fine style too. They both have advantages and disadvantages but all and all, they usually produce the same end result.
As bjornd pointed out, monkey-patching is a problem only when there are multiple libraries involved. Therefore its not a good practice to do it if you are writing reusable libraries. However, it still remains the best technique out there to iron out cross-browser compatibility issues when using host objects in javascript.
See this blog post from 2009 (or the Wayback Machine original) for a real incident when prototype.js and json2.js are used together.
There is an excellent article from Nicholas C. Zakas explaining why this practice is not something that should be in the mind of any programmer during a team or customer project (maybe you can do some tweaks for educational purpose, but not for general project use).
Maintainable JavaScript: Don’t modify objects you don’t own:
https://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/
In addition to the other answers, an even more permanent problem that can arise from modifying built-in objects is that if the non-standard change gets used on enough sites, future versions of ECMAScript will be unable to define prototype methods using the same name. See here:
This is exactly what happened with Array.prototype.flatten and Array.prototype.contains. In short, the specification was written up for those methods, their proposals got to stage 3, and then browsers started shipping it. But, in both cases, it was found that there were ancient libraries which patched the built-in Array object with their own methods with the same name as the new methods, and had different behavior; as a result, websites broke, the browsers had to back out of their implementations of the new methods, and the specification had to be edited. (The methods were renamed.)
For example, there is currently a proposal for String.prototype.replaceAll. If you ship a library which gets widely used, and that library monkeypatches a custom non-standard method onto String.prototype.replaceAll, the replaceAll name will no longer be usable by the specification-writers; it will have to be changed before browsers can implement it.

Is it an anti-pattern to modify JavaScript's built-in prototypes?

I understand from this post, that it's an anti-pattern to modify Object's prototype in JavaScript. I was curious, however, if it's widely considered to be an antipattern to modify other 'built-in' prototypes.
For example: say we wanted to add a setPixel(x,y,color) function to CanvasRenderingContext2D - to abstract out the duty of getting the context's image data, setting a pixel to a certain color, and putting the image data back.
CanvasRenderingContext2D.prototype.setPixel = function(x, y, color){
var imgdata = this.createImageData(1,1);
imgdata.data[0] = color.r;
imgdata.data[1] = color.g;
imgdata.data[2] = color.b;
imgdata.data[3] = color.a;
this.putImageData(imgdata,x,y);
};
I haven't tested this code, but you get the idea. Is something like this against 'best practices' ?
I wouldn't do it as it makes it hard to track what is implemented where. It also introduces the risk of two people overriding the same behavior.
Generally, if you are adding a prototype to one of the base objects in JavaScript, you should have a good reason, and there is really no reason to modify Object, since you don't know how to predict what the end result will be of that modification.
I tend to add startsWith, trim and some other functions to String, for example, as these are helper functions, just as adding some functions to Array that exists for Firefox but not IE just makes sense (such as the filter function).
So, adding to the Canvas is fine, but, what if someone is using your library, and using excanvas. Will it cause a problem?
You either may want to explore that, or document that it doesn't work for excanvas, and if you have a small test to show that, please include it, so that if later there is a new version, and your problem disappears people can verify that.
UPDATE:
You will want to do this:
if (!CanvasRenderingContext2D.setPixel) {
...
}
That way, if someone did include one by that name you will not overwrite it, but you will need to handle it gracefully.
Definitely not; if the method is related to the object in this way then it is an elegant solution.
All of these "anti-pattern" suggestions are not to be blindly adopted. No matter what they say, sometimes the best answer is to bite the bullet and go against convention to get things working. This, of course, largely depends on your scenario.
I'm reminded of a situation where days were spent re-organizing code to make the appropriate fix "the right way", when a simple GO TO would have worked great and only taken a couple of minutes to implement. In the end, a bunch of bugs were created because code was changed that did not need to be changed. Am I a fan of GO TO statements? Hell no! But if using one prevents a months worth of headaches, then there is no question.
I don't see any problem with that as long as the functionality or naming does not override what is already there. I know of some that modify the string prototype for Trim functions.
http://www.somacon.com/p355.php
What is funny, C# now has what's called an Extension Method which effectively does the same thing.
Not as itself, but the Ajax library makers might have problems if they cannot rely on the builtin types and their properties. So you could break code that relies on certain behaviour.

Categories

Resources