2

I would like to create a new object of the type InvertedPeninsula:

var invertedPeninsula = new InvertedPeninsula();

To create this object type, I use an object constructor function:

var InvertedPeninsula = function() {
  this.inhabitants = [
    {
      name: 'Flanery',
      race: 'Human'
    },
    {
      name: 'Sir Charles',
      race: 'Human'
    },
    {
      name: 'Ealei',
      race: 'Elf'
    },
    {
      name: 'Orado',
      race: 'Spector'
    }
  ];

  this.inhabitants.getRace = function(race) {
    var members = [];
    for (var i=0, l = this.length; i < l; i++) {
      if (this[i].race === race) {
        members.push(this[i]);
      }
    }

    return members;
  };

  this.inhabitants.humans = function() {
    return this.getRace('Human');
  };

  this.inhabitants.elves = function() {
    return this.getRace('Elf');
  };

  this.inhabitants.spectors = function() {
    return this.getRace('Spector');
  };
};

In summary, the constructor creates an object with an array called "inhabitants" which itself contains 4 object literals. The InvertedPeninsula object type constructor then goes on to add four function expressions to the inhabitants array. In other words, the inhabitants array contains 4 objects and 4 methods aswell which one may confirm by printing the contents of the array using a "for-in" iterator.

Everything is behaving like I want, but what I am trying to understand is why on earth this constructor can get away with referencing the inhabitants array without mentioning it's name. In particular:

for (var i=0, l = this.length; i < l; i++) 

Should that code above not be:

for (var i=0, l = this.inhabitants.length; i < l; i++) 

Instead?

Similarly, the human, elves and specter array properties all return:

return this.getRace('Human');
return this.getRace('Elf');
return this.getRace('Specter');

respectively. However, should it not rather be:

return this.inhabitants.getRace('Human');
return this.inhabitants.getRace('Elf');
return this.inhabitants.getRace('Specter');

I made these suggested changes but when trying to call any one of the functions, for example:

invertedPeninsula.inhabitants.humans();

I get these errors:

Uncaught TypeError: Cannot read property 'getRace' of undefined

Uncaught TypeError: Cannot read property 'length' of undefined

I am using Google Chrome 55.0.2883.87 m

Any possible explanations as to what is happening under the hood here?

Rob Lyndon
  • 10,563
  • 3
  • 39
  • 57
Dean P
  • 676
  • 8
  • 16
  • Possible duplicate of [How does the "this" keyword work?](http://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work) – Andreas Dec 23 '16 at 15:51
  • "why on earth this constructor can get away with referencing the inhabitants array without mentioning it's name" - It can't, and more importantly, it isn't. In fact, apart from defining the array and adding four method properties to it, the constructor function itself isn't accessing the array at all. The methods that are being added to the array object, however, CAN directly access that array object using `this`, because when they are invoked, `this` will be the nested array object, not the outer peninsula object. – PMV Dec 23 '16 at 16:22

1 Answers1

4

It works because the context when you do this:

invertedPeninsula.inhabitants.humans();

... is not invertedPeninsula, but invertedPeninsula.inhabitants and so any reference to this in the method is to invertedPeninsula.inhabitants.

Note also that you define only one property in the constructor: inhabitants.

All the other methods you define in that constructor are not created on this, but on this.inhabitants, and it is that array object that is getting extended with more methods, not the object being constructed.

trincot
  • 211,288
  • 25
  • 175
  • 211