tags:

views:

201

answers:

2

I was attempting to extend Javascript's Object type with a function that retrieves all of the property names that are part of that object (basically what PHP's array_keys() function does to associative arrays).

Object.prototype.keys = function() {
    var a = [];
    for (var property in this) {
     a.push(property);
    }
    return a;
};

var a = {a:1,b:2,c:3,d:4};
alert(a.toSource());

var b = a.keys();
alert(b.toSource());

When the variable b was alerted, I was expecting to see ["a","b","c","d"], instead I'm seeing ["a","b","c","d","keys"].

It appears the for-in loop is including the prototyped keys() functions.

Is it possible to avoid this behaviour whilst still prototying, or should I just avoid prototyping all together?

+7  A: 

Ahh the joys of extending the built-ins. for..in checks up the entire prototype chain of the object being iterated.

What you need (and I believe this is de-facto now) is a check with hasOwnProperty in your loop:

for (var property in this) {
    if (this.hasOwnProperty(property)) {
        a.push(property);
    }
}

hasOwnProperty ensures that you only get properties that are directly defined on your object (i.e. not up the prototype chain).

Roatin Marth
Cheers, worked a charm!
Lachlan McDonald
Just for your information, `hasOwnProperty` is not supported in IE up to version 5.0 (5.5 has it) and Safari up to 2.02. You may decide that you're not bothered about supporting these browsers but you should know that you're not.
Tim Down
+1  A: 

For most situations, you should avoid extending the native Object.prototype. When you extend it, ALL objects receive those new properties.

Looping over an object properties is a really common task, and as you noted, you were experiencing an unexpected behavior.

I have not seen many people who always use the hasOwnProperty function in their code, I would say that under "controlled environments", maybe being a solo developer working on a project, you could manage those issues, but when the things get bigger, and you have more developers involved, this can cause a lot of problems.

What about making a static function on Object?

Object.keys = function(obj) {
  var a = [];
  for (var property in obj) {
    if (obj.hasOwnProperty(property)) {
      a.push(property);
    }
  }
  return a;
};

var a = {a:1,b:2,c:3,d:4};
alert(a.toSource());

var b = Object.keys(a);
alert(b.toSource());
CMS