1

When using for..in loop along with functions I added to array.prototype, the for..in loops through all those functions as well.

The JavaScript for/in statement loops through the properties of an object

But how do I add to array prototype without it being triggered in the for..in?

Example code:

Array.prototype.sum = function (prop) {
    // Do something
}

for (item in myArray) {
    // Would actually loop myArray.length times + 1 for the 'sum'
}

I'm using a library that uses for..in loop and I can't change that.

user2864740
  • 60,010
  • 15
  • 145
  • 220
AlexD
  • 4,062
  • 5
  • 38
  • 65
  • 1
    You shouldn't, and I don't understand why you want to. – Chrillewoodz Sep 09 '15 at 11:27
  • @Chrillewoodz: I shouldn't what? Use a protoype that way? Wasn't familiar with Object.defineProperty, now I am. – AlexD Sep 09 '15 at 11:39
  • @webdeb: n3-line-chart – AlexD Sep 09 '15 at 11:39
  • 3
    @AlexD: I'd raise a bug with the library authors, there's no excuse for using `for-in` on an array without a `hasOwnProperty` check at a minimum. – T.J. Crowder Sep 09 '15 at 11:40
  • or they better should use length instead, if its allways an array, for in returns the index or the key, not the value, so it can be done less expensive -> var l = arr.length; while(!(0 > l)) {l--;} – webdeb Sep 09 '15 at 11:42

2 Answers2

3

use Object.defineProperty, you can add something to Array.prototype while make it not enumerable, thus not be a target in for-in loop.

Though, take it as last resort, for-in should be avoid if possible.

// This appears in for-in
Array.prototype.sum = function (prop) {
    // Do something
}

// This won't appears in for -in loop.
Object.defineProperty(Array.prototype, 'add', {
  value: function() {
    console.log('OOPSS');
  },
  
  // enumerable is default to false, and it decide whether the attr would be  a target
  // in for-in loop or not.
  enumerable: false
});

var myArray = [1, 2, 3];

for (item in myArray) {
  console.log(item);
    // Would actually loop myArray.length times + 1 for the 'sum'
}

// Check `add` did added to array.prototype.
myArray.add();
fuyushimoya
  • 9,715
  • 3
  • 27
  • 34
3

But how do I add to array prototype without it being triggered in the for..in?

By adding it as a non-enumerable property, via Object.defineProperty. Non-enumerable is the default, so:

Object.defineProperty(Array.prototype, "sum", {
    value: function() {
        // ...implementation of sum
    }
});

...but if we wanted to be explicit, we'd have enumerable: false:

Object.defineProperty(Array.prototype, "sum", {
    enumerable: false, // <== Not necessary, false is the default
    value: function() {
        // ...implementation of sum
    }
});

Live Example:

Object.defineProperty(Array.prototype, "sum", {
  value: function() {
    return this.reduce(function(acc, value) {
      return acc + value;
    }, 0);
  }
});

var a = [1, 2, 3];
snippet.log("a.sum() = " + a.sum());
var key;
for (key in a) {
  snippet.log("key = " + key);
}
snippet.log("Note that 'sum' did not show up as a key");
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

All modern browsers support Object.defineProperty, which was part of the 5th edition specification released in 2009. IE8 does not, as it predates the specification.


Side note: I don't recommend using for-in to loop through arrays, whether you add non-entry properties to Array.prototype (or the array itself) or not. I recommend the various alternatives in this answer. But you've said you're using a library that does it and doesn't have the necessary hasOwnProperty check (which makes it a poorly-written library) and can't change that, so... The above is how you add to Array.prototype without the additions showing up in for-in.

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875