22

I want to define helper methods on the Array.prototype and Object.prototype. My current plan is to do something like:

Array.prototype.find = function(testFun) {
   // code to find element in array
};

So that I can do this:

var arr = [1, 2, 3];
var found = arr.find(function(el) { return el > 2; });

It works fine but if I loop over the array in a for in loop the methods appear as values:

for (var prop in arr) { console.log(prop); }
// prints out:
// 1
// 2
// 3
// find

This will screw up anybody else relying on the for in to just show values (especially on Objects). The later versions of javascript have .map and .filter functions built into arrays but those don't show up on for in loops. How can I create more methods like that which won't show up in a for in loop?

2
  • 2
    That is just the reason for not using for-in loops on arrays! Commented Nov 8, 2012 at 19:38
  • 2
    Don't modify objects you don't own. Commented Nov 8, 2012 at 19:42

4 Answers 4

28

It's quite easy: Don't use for-in loops with Arrays. Blame everybody else who does so - here is a nice snippet to tell them during development.

Of course, if one does an enumeration in a generic function and doesn't know whether he gets an array, a plain object or an object with a custom prototype, you can use hasOwnProperty like this:

for (var prop in anyObj )
    if (Object.prototype.hasOwnProperty.call(anyObj, prop))
        // do something

Notice the explicit use of Object.prototype to get the function - there might be objects that overwrite it (especially in data-maps, the value might not even be a function), objects that do not support it or objects that do not inherit from Object.prototype at all. See also here.

Yet, only a script author who is aware of the problem would filter all his for-in-loops - and some only do it because it gets recommended - and does it mostly wrong, he should have used a for-loop array iteration instead. But our problem are those authors who do not know of it.

An interesting, but Mozilla-only approach would be overwriting the behavior of enumerations on arrays via __iterate__, as demonstrated here.

Fortunately, EcmaScript 5.1 allows us setting properties to be non-enumerable. Of course, this is not supported in older browsers, but why bother? We'd need to use es5-shims anyway for all the cool higher-order array stuff :-) Use defineProperty like this:

Object.defineProperty(Array.prototype, "find", {
    enumerable: false,
    writable: true,
    value: function(testFun) {
        // code to find element in array
    }
});
Sign up to request clarification or add additional context in comments.

7 Comments

I like putting that long hasOwnProperty in a helper function. Having a shorthand like hop(anyObj, prop) makes me less likely to use one of the shorter but suboptimal alternatives that you mentioned.
Sure, you can define var hop = Function.prototype.call.bind(Object.prototype.hasOwnProperty), but as the average programmer should never need it I've not mentioned this. Those libraries which provide highly generic functions use it internally, of course.
@Bergi, I tried to use your last code block with "Object.prototype" rather than "Array.prototype", but no good. Any thoughts? :O
@Fattie No good for what, for defining Array.prototype.find?
cheers @Bergi, I'm just trying to add a dot function on "any object" (when messing with json). So imagine a macro like function handy(athing, "blah") { return athing[blah]? etc etc } I'm really just trying to convert it to a dot function, hence athing.handy("blah") I mean I'm perfectly OK with writing it the first way but as a curiosity, trying to make it a dot function. (I know how to add a dot function to "my" objects; I'm trying to apply a dot function to any object - again more as a curiosity - thanks!)
|
7

Depending on your restrictions:

// In EcmaScript 5 specs and browsers that support it you can use the Object.defineProperty
// to make it not enumerable set the enumerable property to false
Object.defineProperty(Array.prototype, 'find', {
    enumerable: false,  // this will make it not iterable
    get: function(testFun) {
       // code to find element in array
    };
});

Read more about Object.defineProperty here https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty

2 Comments

No, you don't want to define a getter function for this prototype method. Have a look at my answer...
You can't trust anyone else using your code will do the hasOwnProperty check, so this is a safe way.
3

The above answers miss a point:

enumerable ... Defaults to false. (mdn)

So simply using Object.defineProperty(Array.prototype, 'myFunc' myFunc) instead of Array.prototype.myFunc = myFunc will resolve the issue.

1 Comment

This syntax doesn't work. It leaves Array.myFunc undefined.
-1

It's because have to check for hasOwnProperty:

for (var prop in arr) { 
  if (arr.hasOwnProperty(prop)) { 
    console.log(prop) 
  }
}

Now this logs 1, 2, 3.

2 Comments

bergi was trying to say that this code breaks if someone adds a "hasOwnProperty" property to arr or its prototype.
Really bad advice

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.