0

I am experimenting with an inheritance model which combines the dynamic prototype pattern with prototype chaining. This investigation is purely academic as an attempt to understand the properties of objects in JavaScript. I'm sure better models could be recommended. I am curious as to why the combination of these two methods is failing to produce the prototype chain I am looking for.

function Animal(params) {
  this.species = undefined;
  this.sound   = undefined;
  if (params.species !== undefined){ this.species = params.species; }
  if (params.sound   !== undefined){ this.sound   = params.sound;   }
  if (Animal.prototype.makeSound === undefined) {
    Animal.prototype.makeSound = function() {
      console.log(this.sound);
    };
  }
}

function Bear(params) {
  if (params.species === undefined){ params.species = "Bear"; }
  if (params.sound   === undefined){ params.sound   = "Grr";  }
  Animal.call(this, params);
  if (Bear.prototype.swipe === undefined) {
    Bear.prototype = new Animal(params);
    Bear.prototype.swipe = function() {
      console.log("swipe - '" + this.sound + "'");
    };
  }
}
var bear   = new Bear({});
bear.swipe();     // Uncaught TypeError: bear.swipe is not a function(…)
bear.makeSound(); // Uncaught TypeError: bear.makeSound is not a function(…)

If I move the prototype definitions outside of the constructor (ie no longer dynamic pattern) this works fine - with a removal of the params in the chaining call & a check around the params use in the Animal constructor :

function Animal(params) {
  this.species = undefined;
  this.sound   = undefined;
  if (params !== undefined) {
    if (params.species !== undefined){ this.species = params.species; }
    if (params.sound   !== undefined){ this.sound   = params.sound;   }
  }
}
Animal.prototype.makeSound = function() {
  console.log(this.sound);
}

function Bear(params) {
  if (params.species === undefined){ params.species = "Bear"; }
  if (params.sound   === undefined){ params.sound   = "Grr";  }
  Animal.call(this, params);
}
Bear.prototype = new Animal();
Bear.prototype.swipe = function() {
  console.log("swipe - '" + this.sound + "'");
}
var bear   = new Bear({});
bear.swipe();     // swipe - 'Grr'
bear.makeSound(); // Grr

I think this is reason to not bother with the dynamic pattern in real projects. I'm just curious is all.

The full example of what I am trying to do is here

m.s.
  • 16,063
  • 7
  • 53
  • 88
Joe Sinfield
  • 546
  • 4
  • 8
  • Well, doing anything to the `.prototype` inside the constructor happens after the instance had been created. It's just too late. This will never work. Not sure why you call it a "dynamic pattern". – Bergi Oct 09 '15 at 10:52
  • Or actually, [this.constructor.prototype — can't wholly overwrite, but can write individual props?](http://stackoverflow.com/q/21296559/1048572) might have been the better duplicate. Oh, and [don't use `.prototype = new Parent()` for inheritance](http://stackoverflow.com/a/17393153/1048572?Benefits-of-using-Object.create-for-inheritance) anyway. – Bergi Oct 09 '15 at 10:57
  • Thanks for the comments @Bergi. The dynamic pattern is an accepted pattern for defining the prototype within the constructor. See http://www.hostmasterzone.info/dynamicprototype.html. I don't believe that this is a duplicate of either of the suggestions. In the first instance I am not assigning the prototype on every constructor call, just the first (as the pattern dictates), while in the second, I am not attempting to modify the instance constructor property using 'this.', but rather set the prototype's properties directly using 'Animal.'. – Joe Sinfield Oct 09 '15 at 11:15
  • Regarding the .prototype = new Parent(); method. I am assuming you are suggesting using prototypal inheritance instead. Agreed, that is better - but as I said, I am just looking at this academically. This was considered a legitimate method of prototype chaining some time ago and I would like to understand how it works. – Joe Sinfield Oct 09 '15 at 11:23
  • 1
    No, it really is a dupe of the second, notice that `this.constructor` is exactly `Bear`. Doesn't matter whether its `Bear.prototype = …` or `this.constructor.prototype = …`, the problem is exactly the same. – Bergi Oct 09 '15 at 11:33
  • 1
    That "Dynamic Prototype Pattern" (didn't even know there was a name for this antipattern) is horrible, not accepted. Don't use it. Have a look at the [module pattern](http://stackoverflow.com/a/28256166/1048572) instead. – Bergi Oct 09 '15 at 11:36
  • 1
    The `.prototype = new Parent()` method never really was legitimate. It was just widespread. See http://stackoverflow.com/q/12592913/1048572 for details. – Bergi Oct 09 '15 at 11:37
  • Aha. That's a good point. Thanks I'm going to contemplate this for a minute and see if that explains everything. I just need to understand why it works for Animal, but not during the inheritance stage for Bear. – Joe Sinfield Oct 09 '15 at 12:04
  • Ok lots to look up there. Thank you very much. Any chance we can change the marked duplicate to the second one you suggested or is that a ball-ache? – Joe Sinfield Oct 09 '15 at 12:07
  • Needs someone else with a js gold badge, I can only self-handedly reopen but not change the target. The comments are probably explanatory enough :-) – Bergi Oct 09 '15 at 12:15
  • 1
    Ok got it now. The new operator creates a bear object immediately, who's _proto_ references the original Bear.prototype property reference. When this reference changes to Animal in Bear.prototype it is too late. the bear instance is still pointing at the old reference which never had any methods added to it. If we create a var bear2 then everything works because the prototypes have now been set up the way they should have been initially. Many thanks @Bergi for identifying the common problem buried inside the example. – Joe Sinfield Oct 09 '15 at 15:41

0 Answers0