3

I have the following:

function Person() {
  console.log('person');
}

function Player() {
  this.personConstructor();
}

Player.prototype = Person.prototype;
Player.prototype.constructor = Player;
Player.prototype.personConstructor = Person.prototype.constructor;

new Player();

The intent is to inherit from Person into Player then have that new child class call the parent's original constructor. However, this causes an infinite loop. What am I doing wrong and why is the loop happening?

bcardarella
  • 4,667
  • 4
  • 29
  • 45
  • 3
    `Player.prototype = Person.prototype;` should be `Player.prototype = Object.create(Person.prototype);` – cookie monster Aug 09 '14 at 17:04
  • 1
    ...this is because when you made `Player.prototype` a *reference* to `Person.prototype` and then assigned `Player` to `Player.prototype.constructor`, you were actually assigning it to `Person.prototype.constructor`. So then `personConstructor` was actually a reference to `Player` instead of `Person`, and so created the infinite call loop. – cookie monster Aug 09 '14 at 17:06

2 Answers2

4

This line right here is your problem:

Player.prototype = Person.prototype;

You want the prototype for Player to inherit from Person's prototype, but not make them equal. Currently, your code makes the Player and Person prototypes reference equal, so any changes to Player.prototype also affect the Person.prototype (effectively making them indistinguishable).

You're looking for:

Player.prototype = Object.create(Person.prototype);

Object.create instantiates a new object with a given prototype without actually calling the constructor (unlike a regular new Person() call would do). This allows you to get a new object inheriting the Person prototype, which you can then modify for the specifics of a Player.

EDIT: As suggested in the comments by Siddarth, an even better solution would be to set the constructor through a property descriptor:

Player.prototype = Object.create(Person.prototype, {
    constructor: { value: Player }
});

This way, the newly created prototype will have its constructor property made non-configurable, non-enumerable and non-writable. This prevents you from accidentally changing it afterwards through an assignment (e.g. Player.prototype.constructor = Foo) and it won't show up in Object.keys or a for..in loop. Normally, this shouldn't matter much but it's a good practice.

Mattias Buelens
  • 19,609
  • 4
  • 45
  • 51
  • I would make that Object.create(Person.prototype, { constructor: { value: Player } }) – Siddharth Aug 14 '14 at 02:24
  • Also, don't forget to call the parent's constructor in the child's constructor. – Siddharth Aug 14 '14 at 02:26
  • 1
    @Siddharth Oh right, you can pass in a bunch of property descriptors to `Object.create`. I personally prefer using classical assignment or something like `_.extend` for adding properties to a prototype. Property descriptors may be better though, your solution prevents the prototype's `constructor` from (accidentally) getting changed later on. Thanks for the tip! – Mattias Buelens Aug 14 '14 at 15:56
0
function Person(args) {
  console.log('person');
  args=args||{};
  this.name=args.name||"no name";
}

function Player(args) {
//  this.personConstructor();
// I prefer 
  Person.call(this,args);
}

Player.prototype = Object.create(Person.prototype);
Player.prototype.constructor = Player;

console.log(new Player());
console.log(new Player({name:"Jon"}));

It may be better to just re use Parent constructor with the following

Person.call(this,args);

Because if you use something like this.parent()... you'll get an infinite loop when you inherit 3 levels deep.

More on prototype and constructor functions here.

Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
  • 1
    `this.personConstructor()` works fine, no need for `.call` if you're going to pass in the same `this`. However, `Person.call(this,args)` is indeed a more general solution which doesn't need the extra `Player.prototype.personConstructor` definition. – Mattias Buelens Aug 13 '14 at 16:00
  • @MattiasBuelens Correct, I changed the answer. – HMR Aug 14 '14 at 12:29