0

I'm working with a JavaScript application that uses Underscore.js to handle a lot of inheritance. The standard pattern for object definition is one where the basic object is defined in a variable and then its prototype is defined directly afterwards, like this:

var Parent = function()
{
    this.name="parent";
    this.loadData();
};
Parent.prototype = {
    loadData: function() 
    {
         this.say="parent loadData";   
    },
    action: function()
    {
       return this.name+" says: '"+this.say+"'";   
    }

};

This works fine and is reasonably easy to read, but I am a little confused by the way that inheritance is treated in the system. The authors have used the _.extend method to create a child object in the same format, like this:

var Child = function()
{
    this.name="child";
    _.extend(this, new Parent());    

};
Child.prototype= {
    loadData: function()
    {
         this.say="child data loaded";   
    }
});

The problem is that although core properties seem to be copied alright, I appear to have the parent rather than child's methods.

var myParent = new Parent();
var myChild = new Child();
console.log( myParent.action() ); // "parent says: 'parent loadData'"
console.log( myChild.action() );  // "child says: 'parent loadData'"

I tried a _.extend( Child.prototype, { ...etc but that didn't seem to help. Now I could go back to the way I have handled inherited properties in the past Child.prototype.loadData=function() but it would break the idiom used through the rest of the application, so I'm wondering: Is this is a standard approach to JavaScript object orientation? If so does it have a name/some documentation anywhere? Is there a way to keep this model and have practical inheritance working correctly while retaining readable code?

glenatron
  • 11,018
  • 13
  • 64
  • 112
  • 1
    `_.extend(new Parent(), this);` is total rubbish. – Bergi Jul 11 '14 at 14:30
  • And therefore: No, this is not a standard approach. – Bergi Jul 11 '14 at 14:34
  • @Bergi good to know. _Why_ is it total rubbish? Obviously this is a very simplified illustration- most of the functions here are fairly complex and it does ensure at least attribute-level inheritance. What I'm not really following is why the original designers would have chosen this approach, but they seem generally competent, so I don't want to go around breaking idioms if the problem is with my understanding. – glenatron Jul 11 '14 at 14:34
  • Because it doesn't work. To inherit you can inherit parent's instance specefic members with `Parent.call(this,arguments)` and to set prototype part of inheritance you can do `Child.prototype=Object.create(Parent.prototype);` http://stackoverflow.com/a/16063711/1641941 – HMR Jul 11 '14 at 14:40
  • @glenatron: See my answer. Could you put a link to where you've seen this pattern? The designers don't really seem to be competent. – Bergi Jul 11 '14 at 14:44
  • @Bergi I was trying to figure out why it wasn't behaving the way I expected and didn't pay attention to the return value of the `_.extend` so I switched the order in jsfiddle when I was experimenting with it. The original is ordered in the way that does make _more_ sense. – glenatron Jul 11 '14 at 14:55

1 Answers1

2
_.extend(new Parent(), this);

Urgh! Check the underscore docs on what this does: it creates a new Parent instance and copies the name property (and maybe some inherited ones) to it. Then, it will throw that object away. Why? I don't know.

There is a pattern called parasitic inheritance that works a little like mixins, which at least returns that amended object to become the "child instance". But as it stands, this is totally useless.

What would be acceptable?

_.extend(this, new Parent());

would copy the properties on the actual child instance. But there an even easier solution - just create them on the new instance right away:

Parent.call(this);

I tried a _.extend( Child.prototype, { … but that didn't seem to help.

Well, it was a step in the right direction. Next to that "super" call from above, you also want to have the shared (prototype) properties of the parent on the child's prototype. You can do

_.extend(Child.prototype, Parent.prototype, {
    loadData: …
});

to copy them (and then the new, child-specific properties) onto the prototype object, but the standard solution is to create an object that (dynamically, prototypically) inherits from Parent.prototype:

Child.prototype = _.extend(Object.create(Parent.prototype), {
    loadData: …
});
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Ah... ahem... this top part might be me being a dumbass in jsfiddle because I was trying to make things work. Editing the question now... – glenatron Jul 11 '14 at 14:48
  • Thanks, this is a really useful answer and _Parasitic Inheritance_ was the term I needed to be able to figure out more about the process. – glenatron Jul 11 '14 at 15:15