11

I am reading MDN docs for better understanding javascript. This is an excerpt from there

Object.prototype.objCustom = function() {}; 
Array.prototype.arrCustom = function() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';

for (let i in iterable) {
  console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
}

In the worst case i thought it is going to print 0, 1, 2, "foo", "arrCustom" but it also prints objCustom.

Update:
1) How can i visualize the prototype chain from iterable to Array all the way upto Object. Like is there any iterable.getParent method or iterable.myparent property which points to parent on it.
2) why it does not print array functions such as toString, sort they are also on Array.prototype.
3) Do i need to use hasOwnProperty always when someone add something to Array.prototype property.

Community
  • 1
  • 1
Srinivas
  • 464
  • 3
  • 17
  • `Array` inherits from `Object`? – Ry- Aug 24 '17 at 00:24
  • Thanks for comment, how does it go from `variable iterable` all the way upto `Object`, that's what i want to know more with code. – Srinivas Aug 24 '17 at 00:28
  • for in iterates over all enumerable properties of an object, since you did not define the properties as being non-enumerable they will be iterated over – Patrick Evans Aug 24 '17 at 00:30
  • I'm not sure I understand your first question. What do you mean by "*How can i see connection*"? `iterable instanceof Array`/`iterable instanceof Object` will show that they inherit from these. If you don't know what an object inherit from but want to find out, use `Object.getPrototypeOf(iterable)`. – Bergi Aug 24 '17 at 00:51
  • @Bergi updated the question – Srinivas Aug 24 '17 at 00:56
  • @Srinivas Ah, thanks. As I said, `Object.getPrototypeOf` can be used for that. See also the duplicate questions. – Bergi Aug 24 '17 at 01:05
  • @Bergi Is there any way to go from bottom to up, and `Object.getPrototypeOf(iterable)` gives me `[]` i dont know what to do next? this time i need spoon feeding. – Srinivas Aug 24 '17 at 01:09
  • @Srinivas Continue to call `Object.getPrototypeOf` on that until you reach `null` (the end of the chain). See the third duplicate for example code. – Bergi Aug 24 '17 at 01:14
  • Thanks @Bergi for supportive information, can i make my `arrCustom`, `objCustom` non-enumerable? – Srinivas Aug 24 '17 at 01:16
  • @Srinivas Yes. See the first duplicate for how to do that. – Bergi Aug 24 '17 at 01:16
  • Can you add one answer with proper explanation to address all these questions. – Srinivas Aug 24 '17 at 01:24
  • @Srinivas: I answered all of your questions below. Is there something lacking? – spanky Aug 24 '17 at 01:55
  • @Srinivas Nice Question, I had same doubts. – Suraj Jain Apr 13 '19 at 08:30

3 Answers3

7

It's because a for in loop is meant to iterate all enumerable properties, both owned and inherited. You can use Object.keys() to get only owned properties.

Object.prototype.objCustom = function() {}; 
Array.prototype.arrCustom = function() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';

Object.keys(iterable).forEach(function(key) {
  console.log(key); // logs 0, 1, 2, "foo"
});

However, it's pretty unusual to put non-numeric properties on an Array. There are usually better ways to accomplish whatever it is you need to do.

It's also unusual to use a for in loop on an Array. There are other, better ways of iterating an Array, as long as you keep it limited to its numeric indices.


1) "How can i visualize the prototype chain from iterable to Array all the way upto Object. Like is there any iterable.getParent method or iterable.myparent property which points to parent on it."

You can use Object.getPrototypeOf() to get the next object an object inherits from. Do it in a loop to go until null is reached.

var proto = Object.getPrototypeOf(myObj);
do {
  console.log(proto);
} while((proto = Object.getPrototypeOf(proto)));

2) "why it does not print array functions such as toString, sort they are also on Array.prototype."

toString() and other built in methods are non-enumerable. As I noted above, for in only reaches enumerable properties.

3) "Do i need to use hasOwnProperty always when someone add something to Array.prototype property."

Don't use for in loops on Arrays, and you'll do better. But yes, if you use for in, you'll need .hasOwnProperty() boilerplate code to guard against that.

You should also be aware that there's no guarantee that Array indices will be reached in order when using for in. And it's also usually much slower that other means of Array iteration. It's especially slow with the .hasOwnProperty() check.

spanky
  • 2,768
  • 8
  • 9
  • Thanks for answering all the question, it helped me a lot. Can I ask from where have you learned all this, Can you point me to a decent source. – Suraj Jain Apr 13 '19 at 08:32
4

Because for-in iterates through all the properties of the object, including the ones inherited from the prototype. To skip the inherited properties, use hasOwnProperty().

Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';

for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {
    console.log(i);
  }
}
Barmar
  • 741,623
  • 53
  • 500
  • 612
1

for..in loops through all the properties that an object has, this means own properties and inherited. That is why for..in is ussually accompanied by hasOwnProperty, like this

Object.prototype.objCustom = function() {}; 
Array.prototype.arrCustom = function() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';

for (let i in iterable) {
  if (iterable.hasOwnProperty(i))
    console.log('own', i); // logs 0, 1, 2, "foo"
  else
    console.log('not', i); // logs "arrCustom", "objCustom" (inherited)
}
jperelli
  • 6,988
  • 5
  • 50
  • 85