views:

401

answers:

3

I have added a simple .js to my page that has some pretty mundane common-task sort of functions added to the Object and Array prototypes.

Through trial and error I've figured out that adding any function to Object.prototype, no matter it's name or what it does causes javascript errors in jQuery:

The culprit?

Object.prototype.foo = function() {
    /*do nothing and break jQuery*/
};

The error I'm getting line 1056 of jquery-1.3.2.js, in the attr:function { } declaration:

/*Object doesn't support this property or method*/
name = name.replace(/-([a-z])/ig, function(all, letter) {
            return letter.toUpperCase();
        });

Apparently G.replace is undefined.

While it's obvious that there's something I'm just not wrapping my head around with prototyping, I'm failing miserably to figure out what it is.

To be clear, I'm not looking for a workaround, I have that handled... what I'm looking for is an answer to 'Why?'. Why does adding a function to Object.prototype break this bit of code?

+1  A: 

I doubt adding a function to Object.prototype breaks jQuery directly. Just make sure each for..in loop you have throughout your site is wrapped in a hasOwnProperty check, since you've add the function globally and the result of iterating over it can be unpredictable:

Object.prototype.foo = function() {};    
var myObject = {m1: "one", m2: "two" };

for(var i in myObject) { if(myObject.hasOwnProperty(i)) {
   // Do stuff... but not to Object.prototype.foo
}}
jshalvi
Well if I comment out the Object.prototype.foo declaration, everything works just fine. Also, at the point it's breaking, it hasn't even reached any of my code beyond that foo declaration.
blesh
You're right it does not break jQuery directly, it breaks Javascript!
Josh Stodola
Depends on how you look at it. Ideally you should be able to extend Object with no problem, but in reality, yeah it's a bad idea and there's rarely a good reason for it. Crockford sees enumerating over functions added to the prototype as a "mistake in the language" and so the best practice is to be defensive and ALWAYS add hasOwnProperty to for..in loops. It sucks, but I do it religiously ;)
jshalvi
+1  A: 

You should never extend Object.prototype. It does far more than break jQuery; it completely breaks the "object-as-hashtables" feature of Javascript. Don't do it.

You can ask John Resig, and he'll tell you the same thing.

Josh Stodola
Extending `Object.prototype` is fine. The caveat is to use `hasOwnProperty` in `for..in` loops. It's supported in every major browser including Safari since 2.0. It's just laziness that jQuery does not do it in its `for..in` loops. The performance impact is negligable, and Resig knows this: http://osdir.com/ml/jquery-dev/2009-02/msg00543.html Just my opinion however.
Crescent Fresh
Interesting stuff. Thanks. I wonder why it breaks the "objects-as-hashtables" thing.... hmmm.
blesh
@Crescent It's much deeper than that. Sure you can work around the problem with `for...in` loops like that, but having object-as-hashtables in Javascript does lots of other things. For example, `toString`, `valueOf` and others are not enumerated. This *does* have an impact. Also, when you are the lead dev of a library used by tons of people, you can't blame his decision on laziness. I think a better word would be cautious.
Josh Stodola
@Josh: perhaps lazy is a bit harsh, you're right. I don't get your point about `toString` and `valueOf`. Yes they are marked `DontEnum`, so they don't show up in `for..in`. How does that invalidate the effectiveness of `hadOwnProperty`?
Crescent Fresh
*`hasOwnProperty` that is.
Crescent Fresh
+1  A: 

I agree, adding something to Object.prototype demands caution and should be avoided. Look for other solutions such as:

Adding it to Object and then accessing it with a call or apply, as needed. For example:

Object.foo = function () { return this.whatever()}

Then call that on an object by:

Object.foo.call(Objname);  // this invokes the function as though it were a
                           // method of Objname.  That is, its like Objname.foo()

For fun, you can add the following (yes, I know its a bit dangerous...):

Function.using = Function.call; // syntactic sugar

Now you can write Object.foo.using(Objname) and it reads like a sentance.

But as a rule, stay away from altering any of the big prototypes.

davehamptonusa