Object.prototype is verboten 98

It is not seldom that you see people messing with Object.prototype. This is very bad because it breaks the object-as-hash-tables feature in javascript. Basically, the following is a very common scenario:

var obj = {a: "A", b: "B", c: "C", d: "D"};
for (var key in obj) {
   doSomething(key, obj[key], obj);
}

if ("b" in obj) {
   doSomethingElse();
}

If someone modified the Object.prototype the for in loop would include any fields you’ve added.

So let me say it in plain English: Object.prototype is forbidden and should be treated as sealed / constant / final.

PS. There are other issues with using objects-as-hash-tables in js, like for example toString, valueOf and others are not enumerated. If you need a totally fail safe hash table you need to implement that yourself. I leave that as an excercise to the readers (you can use the object-as-hash-table as a start).

  • http://dean.edwards.name/ Dean Edwards

    Hear hear!

  • http://www.nczonline.net/ Nicholas

    Always has to be on dissenter in the group, right? I think just like everything, Object.prototype has it uses. Granted, some people use it incorrectly, but really it can be very useful is used correctly (that is, to help with inheritance).

    That people try to use Object.prototype for anything other than inheritance is a little odd to me, and that’s typically where you run into trouble. My general rule of thumb: only functions go on a prototype, actual values go on the object itself.

    Just my $0.02

  • http://erik.eae.net Erik Arvidsson

    Why would you use Object.prototype for inheritance? It makes more sense to use YourConstructor.prototype for this.

    Personally I put everything except mutable objects on the prototype of the constructor.

  • http://jan.moesen.nu/ Jan!

    So if the String object doesn’t have the trim() method, and I want to add it to all strings, even ones that are already live, how would I do that?

  • http://erik.eae.net Erik Arvidsson

    Jan: The String.prototype is safe to modify. Not many people use for in loops over string objects (or the in operator for that matter).

    String.prototype.trim = function () {
    return this.replace(/^\s+|\s+$/g, “”);
    };

  • http://blog.monstuff.com Julien Couvreur

    What about removing/deleting all the extra methods that you don’t need, if you need a pure association table? I’m not sure what would be the best way of writing that up though. Any ideas?

  • http://erik.eae.net Erik Arvidsson

    Julien: If you need a hash/associative array then use Object and not Array.

  • Pingback: IE7 and script.aculo.us()

  • Jon Casey

    Just wanted to say, it may be in best interest that any library should work with or with out an Object.prototype being added?

    Yes, it is a nice gesture, to not start off your codebase by interupting the Object.prototype.. BUT, any user that wants to incorporate their own prototype additions should not be denied access..

    In short:

    var object = new Object();
    for(var key in object){
    if(!(key in Object.prototype)){
    // do some stuff SAFELY!
    }
    }

  • http://erik.eae.net Erik Arvidsson

    Jon: I’d rather not do that. If people want to modify a common supper class they should create their own base class.

    function MyObject() {}
    MyObject.prototype.thisIsSage = …;

    Adding the test you are suggesting every time is just asking for trouble.

  • Jon Casey

    Erik: Could you explain any troubles that would present themselves with the following code?

    I myself rarely ever have used the for..in loop for enumeration;

    Yet, as high-level programmers, should we not write code as efficiently as possible and make it as “bullet-proof” as possible?

    The mentioned code bit above is limited.
    But, any for..in loop that I would put in a Super-Library
    would do some small EXTRA bug-proof testing, to be assured any other has not tampered w/ such.

    But, I am speaking as a head developer for developers of highly deployed government client/web applications.

  • Jon Casey

    sorry meant the “above” code! not “following code” :)

  • http://erik.eae.net Erik Arvidsson

    The in operator and for in loops are used a lot since they are used to iterate over items in hashes and hashes are used a lot for lookup tables, caches and what not. Adding the test not only makes the code a lot harder to read, it also slows it down.

    Making things as bullet-proof as possible is not usually a good idea in interpreted dynamic languages because it adds too much overhead. Do you typecheck all your arguments in all your methods? Do you type check all your fields? Just a simple line as this would require a few tests:

    obj.method()

    Someone might have changed “method” so that it isn’t a Function any more or even worse removed obj.

    You have to put some restriction on the use and I’m all for banning changes to Object.prototype.

  • Jon Casey

    I can understand a small hit in performance, yet, find them barely measurable in my test cases.

    “Do you type check all arguments?” No, yet often, some manipulations must be performed to alter, or be assured the proper type was sent, or throw Error for improper arguments/arguments.length required.

    “Do you type check fields?” Validation may be required on MANY of them, but typically the areas are already constrained.

    Often for .. in loop would be done as you mentioned for certain needs, even just doing the simple:
    if(typeof o[key] != ‘function’) // .. process
    can eliminate 90% more complaints, and ease wider compatibility.

  • http://erik.eae.net Erik Arvidsson

    Jon: This very common expression

    s in obj

    becomes the very cumbersome

    s in obj && s != a && s != b && s != c …

    The performance issue is negligable. The increase is complexity on the other hand is unacceptable.

    I just cannot justify modify Object.prototype. I agree that there are a few cases where one might want to modify Object prototype; isEmpty, getKeys and getValues as well as map, filter and other list type methods comes to mind. But for a real world API these needs to be static methods instead.

  • Jon Casey

    I agree on not modifying it by default.
    Just wondered if this could be a stronger way to enumerate.

    for (var p in o)
    if (!(p in Object.prototype))
    this.add(p,o[p]);

    Anyhow, great work, it is nice to see someone working along the same paths.

  • http://www.shaderlab.com Randy Reddig

    Modifying an object’s prototype is fine. The caveat is that you need to put an extra test into your for loops. The test is done with hasOwnProperty(), which unfortunately is unsupported in Safari/WebKit.

    To wit:

    if( !Object.prototype.hasOwnProperty ) {
    Object.prototype.hasOwnProperty = function( property ) {
    try {
    var prototype = this.constructor.prototype;
    while( prototype ) {
    if( prototype[ property ] == this[ property ] ) {
    return false;
    }
    prototype = prototype.prototype;
    }
    } catch( e ) {}
    return true;
    }
    }

    Object.prototype.extend = function( object ) {
    for( var property in object ) {
    try {
    if( !this.prototype[ property ] && object.hasOwnProperty( property ) )
    this.prototype[ property ] = object[ property ];
    } catch( e ) {}
    }
    }

    Once you’ve relived your code of its for-in loop additiction, you’re free to extend the base JavaScript classes with abandon. Good uses of extending base classes include adding toJSON() methods, string escaping, and missing/additional Array helper methods like indexOf() and forEach().

    A favorite of mine, while not modifying any prototype, overloads a constructor function to give IE a consistent interface for XMLHttpRequest:

    if( !window.XMLHttpRequest ) {
    window.XMLHttpRequest = function() {
    var types = [
    "Microsoft.XMLHTTP",
    "MSXML2.XMLHTTP.5.0",
    "MSXML2.XMLHTTP.4.0",
    "MSXML2.XMLHTTP.3.0",
    "MSXML2.XMLHTTP"
    ];

    for( var i = 0; i

  • http://www.shaderlab.com Randy Reddig

    Missing snippet, with < escaped:

    if( !window.XMLHttpRequest ) {
    window.XMLHttpRequest = function() {
    var types = [
    "Microsoft.XMLHTTP",
    "MSXML2.XMLHTTP.5.0",
    "MSXML2.XMLHTTP.4.0",
    "MSXML2.XMLHTTP.3.0",
    "MSXML2.XMLHTTP"
    ];

    for( var i = 0; i < types.length; i++ ) {
    try {
    return new ActiveXObject( types[ i ] );
    } catch( e ) {}
    }

    return undefined;
    }
    }

  • Igor E. Poteryaev

    Randy,

    It is not hard to implement Object.prototype.extend as function,
    not as method.

    var extend = function (object, extender) {

    var props = extender || {}; // fail-safe
    for (var property in props) {
    object.prototype[property] = props[property];
    }
    return object;
    };

    IMHO, cleaner, cross-browser and a lot less intrusive than adding method to Object.prototype.

  • http://erik.eae.net Erik Arvidsson

    Randy: I think you missed the point. The point is that it is bad to do this because the rest of your project takes such a huge complexity hit.

    If your application needs common functionality across classes/objects then extend your own base class instead of messing with Object.

  • http://shaderlab.com Randy Reddig

    No, I just disagree.

    The only “huge complexity” is the addition of a hasOwnProperty() check in a for-in loop, which can be implemented in Safari as I demonstrated.

  • http://erik.eae.net Erik Arvidsson

    … and every time you use the in operator.

    I just don’t see the gain in messing with Object.prototype.

  • http://www.shaderlab.com Randy Reddig

    The in operator functions the same on Array objects, so for-in on an array returns 0 through N-1 for arrays of length N.

    The JavaScript 1.5 methods in Array.prototype you’ve implemented will exhibit the same behavior as modifying Object.prototype, “breaking” the in operator.

    Using the in operator as a test for existence of a hash key is dubious as well, for reasons you described above. JavaScript objects may have properties of hash tables, but they are /not/ hash tables.

    Hence the hasOwnProperty() method.

  • http://erik.eae.net Erik Arvidsson

    But using the in operator on arrays is incorrect anyway. It is a very common mistake even among more experienced programmers:

    var a = [];
    a[3] = 3;
    for (var i in a) {
    alert(i)
    }

    The length above is 4 but you don’t get 0, 1 or 2. The above code is semantically the same as this:

    var a = {};
    a[3] = 3;
    for (var i in a) {
    alert(i)
    }

    I agree with you regarding the hasOwnProperty method. To make a robust hash table you would need to prefix the field name or use hasOwnProperty. I didn’t want to post this here but here is one simple implementation:

    function HashTable() {
    this._hash = {}
    }

    HashTable.prototype.add = function (key, val) {
    this._hash[key] = val;
    };

    HashTable.prototype.remove = function (key) {
    delete this._hash[key];
    };

    HashTable.prototype.getItem = function (key) {
    if (this._hash.hasOwnProperty(key)) {
    return this._hash[key];
    }
    return undefined;
    };

    HashTable.prototype.containsKey = function (key) {
    return this._hash.hasOwnProperty(key);
    };

    HashTable.prototype.getKeys = function () {
    var res = [];
    for (var k in this._hash) {
    if (this._hash.hasOwnProperty(k)) {
    res.push(k);
    }
    }
    return res;
    };

    HashTable.prototype.getValues = function () {
    var res = [];
    for (var k in this._hash) {
    if (this._hash.hasOwnProperty(k)) {
    res.push(this._hash[k]);
    }
    }
    return res;
    };

  • Pingback: Panasonic Youth » Blog Archive » Prototype and extending javascript’s Object or Array()

  • Pingback: OpenLaszlo Project Blog » Class-based OOP in Javascript done right()

  • Pingback: OpenLaszlo Project Blog » Class-based OOP in Javascript done right()

  • Pingback: OpenLaszlo Project Blog » Class-based OOP in Javascript done right()

  • http://guestbooks.pathfinder.gr/read/mike18 Mike 18

    What does the “unassigned local network address” error mean?

  • Pingback: OpenLaszlo Project Blog » Class-based inheritance in Javascript done right()

  • Pingback: OpenLaszlo Project Blog » Class-based OOP in Javascript done right()

  • http://supersloper.net Jeremy

    I don’t use the prototype.js either… not because of these reasons… I just didn’t want to spend the time to learn it, and it has about zero documentation and almost non-existent commenting.

    But to get around the problem looping over these “object hashes/associative arrays” just keep an array to index the keys. You need this if you want to sort your javascript objects anyways.

    So as you are building your objects of say contacts add to an array too…

    var aContacts = new Array();
    var oContacts = new Object();

    build some contact object stuff stuff keying off the ids
    oContacts[id] = whatever.
    aContacts.push(id);

    then

    var key;
    var nLength = aContacts.length;
    for (var i = 0; i

  • http://supersloper.net Jeremy

    shoot retarded, this blog didn’t escape my greater thans in the loop with HTML entities?

    for (var i = 0; i < nLength; i++)
    {
    key = oContacts[i];
    do stuff with oContacts[key];
    }

    this gets around the prototype.js problem and allows for sorting

  • Pingback: A Base Class for JavaScript Inheritance()

  • Pingback: Ajaxian » Dean Edwards and Another Base.js()

  • Pingback: Painfully Obvious » Blog Archive » JavaScript “Associative Arrays” Considered Harmful()

  • Pingback: alexander kirk » Blog Archive » Misuse of the Array Object in JavaScript()

  • Pingback: Ajaxian » Javascript Associative Arrays considered harmful()

  • Pingback: Twologic » Blog Archive » Ruby OO Continued…()

  • http://www.thomasfrank.se Thomas Frank

    Object.prototype is erlaubt

    So you say that extending the JavaScript Object.prototype is verboten… Now, this does not go well with my lazy attitude to programming. But neither does the extra checks needed in your for-in-loops if you extend it. Here’s my lazy solution.

    http://www.thomasfrank.se/object_prototype_is_erlaubt.html

  • Pingback: Dean Edwards: Object.prototype is erlaubt()

  • http://mwarden.f2o.org Matt Warden

    You have people using Object as a hashtable instead of implementing their own data structure like they should, and you believe the solution is to forbid extending Object.prototype? Nonsense. The solution is to implement the data structure. That’s the solution irregardless of problems introduced by Object.prototype modification.

  • http://www.thomasfrank.se Thomas Frank

    @Dean: Glad you like my headline so much that you chose to reuse it: http://www.thomasfrank.se/object_prototype_is_erlaubt.html

    And that you give yet another example that shows that it is not a problem to extend the Object.prototype.

  • http://erik.eae.net Erik Arvidsson

    Thomas: If you want to give up ease of programming (for what???) go ahead. Personally I feel that there are lots of good user cases for using plain Object as a simple hash (you have to have full control of the keys but that is very common).

  • Goswinus Odekerke

    I absolutely agree with the statement. I’d like to take it a step further: It’s also verboten to pass an object to a function! Why? Checkout following code:

    function someFunction(o)
    {
    o.e = function() { return “E”; };
    }

    var obj = {a: “A”, b: “B”, c: “C”, d: “D”};

    someFunction(obj);

    for (var key in obj) {
    doSomething(key, obj[key], obj);
    }

    The impertinent function “someFunction” added a function “e” to the newly created object, which will show in the “for .. in” loop!

    I hope you appreciate my sarcasm.

    Just because your application doesn’t require this functionality and/or doesn’t take it in account, doesn’t mean that someone else can’t come up with a valid reason to use it.
    The very fact that Object.prototype is exposed and may be changed, illustrates the power of the language and, more importantly, invites to use it to the max! It also implies that any application should always be aware of this feature and take in account that it has been used!
    If your application fails because of this feature, it says something about your application, and not about the feature.

  • Michael

    I have to agree that it is a Bad Thing to modify Object.prototype behind a program’s back. Which is a very different matter than the one Goswinus Odekerke lays out, where an object is passed to a function whose purpose is to modify that object in-place. You /can/ seriously make the case that functions should never modify their arguments in-place, but that is different than saying a library should never silently change the base class of every object in every other script and library in-place.

    However, I have to add that the reason this is bad is because of how people use the “for (p in o)” functionality. Objects have properties, but that doesn’t make them Hash Tables. Okay, sure, they can be used like Hash Tables, but *that* is when you get into trouble: that is, the assumption that Object will only ever have members that you yourself added to it. Enumerating over an object’s members is useful for introspection, but can’t (as we’ve seen) be relied on for clean data storage. Instead, if you actually need a Hash Table, you should be using a Hash Table object, with accessors to return the data items inside (like Erik’s example).

    Unfortunately the de facto coding practice is broken, with people abusing their generic Objects to do the work of specific Hash Tables. The conclusion being that, if you want to play nicely with all those coders, you shouldn’t modify their Object.prototypes.

  • http://httpete.com httpete

    Here is how to do this in prototypejs fashion.

    Object.extend(String.prototype, {

    trim: function(){
    return this.replace(/^\s+|\s+$/g, ”);
    }
    });

  • Pingback: Crisp’s blog » Blog Archive » .toJSONString() and Object.prototype()

  • Pingback: Ruth Martinez()

  • Pingback: Anthony Adams()