1

Say I have an object person like so

var person = {
  firstname: 'Default',
  lastname: 'Default',
  getFullName: function() {
    return this.firstname + ' ' + this.lastname;
  }
}

I make a new object john and set its prototype to that of person

var john = {
  firstname: 'John',
  lastname: 'Doe'
}

john.__proto__ = person;

if I console.log john, I see a tree structure like so

enter image description here

where we see getFullName embedded under __proto__. Now here comes the surprise

for (var prop in john) {
  console.log(prop)
}

returns

enter image description here

Even though getFullName was one level deep, somehow, the loop was able to find it.

Now, compare that to

var obj = {a: 1, b: {c:2, d: 3 }}
for (var prop in obj) {console.log(prop)}

which behaves as I expect, which is that c and d were not automagically found by the loop

enter image description here

So how is it that in the former case the loop traversed deeper in to the tree to dig up embedded properties while in the latter it did not?

thetrystero
  • 5,622
  • 5
  • 23
  • 34
  • Possible duplicate of [Javascript: hiding prototype methods in for loop?](http://stackoverflow.com/questions/1107681/javascript-hiding-prototype-methods-in-for-loop) – Michał Perłakowski Jan 16 '16 at 06:50

1 Answers1

4

The for..in loop will iterate all the own enumerable properties, as well as the inherited enumerable properties. That is why you are seeing getFullName in the loop.

But in the second case, it lists only the properties of obj, which are actually a and b only (c and d are properties of object b).


This is corresponding section from the language specification, for for..in and it uses the internal [[Enumerate]] slot.

The internal [[Enumerate]] slot will be used to enumerate the object passed to it which gives the enumerable properties of the object. Quoting that section,

Enumerating the properties of the target object includes enumerating properties of its prototype, and the prototype of the prototype, and so on, recursively;

Since the for..in goes up the prototype chain recursively, we are able to see the objects defined in the prototype, in this case getFullName.


If you don't want the getFullName to show up during the iteration, then you can define it as non-enumerable, like this

var person = {
  firstname: 'Default',
  lastname: 'Default'
};

Object.defineProperty(person, 'getFullName', {
  value: function() {
    return this.firstname + ' ' + this.lastname;
  },
  enumerable: false
});

Now when you print the keys, getFullName will not be shown, as it is not enumerable anymore.


Note: Using __proto__ should be avoided, as it is not part of the ECMAScript specifications. Instead, you should use Object.setPrototypeOf.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497