/    Sign up×
Community /Pin to ProfileBookmark

Inheritance with customization

Hi. I’ve got a simple problem I’m trying to solve, but I can’t find a solution that works online. Here’s what I’m trying to do, boiled down:

  • 1.

    Define a class of objects, called Mover, that would basically look like this:
    Mover

  • has x property

  • has y property

  • has move method

  • has Constructor that takes two arguments, x and y, and sets these to the x and y properties of the object.
  • 2.

    Define a second class of objects, let’s say “Cat”, that inherits from Mover, like this:
    Cat

  • Inherits x, y, and move from Mover.

  • has breed property.

  • has a Constructor that takes /three/ arguments {x, y, breed} that sets the breed property to the breed argument, and then passes the arguments to the Mover constructor.
  • 3.

    Call one function that returns a new Cat object.


  • 4.

    Be able to access Cat.move()

  • Does that makes sense? I’ve seen all sorts of examples online, and thy get very complicated very quickly, and I haven’t seen one that actually works! Here’s what I’ve got so far:

    [CODE]function Mover(x, y){
    this.x = x;
    this.y = y;
    // #note_1
    }
    Mover.prototype.move = function(delta_x, delta_y){
    do_stuff();
    };
    function Cat(x, y, breed){
    // #note_2
    this.breed = breed;
    }
    // #note_3
    var my_cat = new Cat(1,2, “tabby”);[/CODE]

    #note_1: There are several ways I could have given the Mover class a move function. I could have done “this.move = function”, but that would create a new function for each object. I could have created a global function with a name like “mover_move” and then set “this.move = mover_move”, but that would clutter the list of global functions. Setting a function on the prototype seems to be the way I should be doing this.
    #note_2: I could call Mover.apply(arguments) here, but that would only take care of the x and y variables. The my_cat variable would not have a move() method.
    #note_3: What am I supposed to be doing here? I feel like I should be doing some sort of complicated Cat.prototype = Mover.something.something.prototype.

    I’m also seeing a lot about Object.create(type, {many_braces: {value: magic_number}}), but 1) that is messy as all hell, 2) the value of many_braces cannot be changed from magic_number in the future, 3) this still doesn’t solve the issue of having multiple constructors called. Alternately, I may just have no clue how to use this feature.

    So here’s my question: what would you write in order to make a Cat object that inherits from Mover?

    to post a comment
    JavaScript

    6 Comments(s)

    Copy linkTweet thisAlerts:
    @Declan1991Apr 14.2012 — [url=http://www.crockford.com/javascript/inheritance.html]Parasitic inheritance[/url] describes exactly how you could do what you want. Really, that's trying to force a pattern on JavaScript that it wasn't really designed for, so you might be better off rethinking how you're going to implement this, I'm not sure without knowing more details.
    Copy linkTweet thisAlerts:
    @Jeff_MottApr 15.2012 — #note_1: There are several ways I could have given the Mover class a move function. I could have done "this.move = function", but that would create a new function for each object. I could have created a global function with a name like "mover_move" and then set "this.move = mover_move", but that would clutter the list of global functions. Setting a function on the prototype seems to be the way I should be doing this.[/quote]

    You did the right thing here. This is the standard practice.

    #note_2: I could call Mover.apply(arguments) here, but that would only take care of the x and y variables. The my_cat variable would not have a move() method.

    #note_3: What am I supposed to be doing here? I feel like I should be doing some sort of complicated Cat.prototype = Mover.something.something.prototype.[/quote]


    The simplest way to make Cat inherit from Mover is to set Cat's prototype to a new instance of Mover.

    [font=courier]Cat.prototype = new Mover();[/font]

    You should still invoke [font=courier]Mover.apply(this, arguments)[/font] from the Cat constructor.

    I'm also seeing a lot about Object.create(type, {many_braces: {value: magic_number}}) ...[/quote]

    Object.create is generally considered a cleaner way to set up inheritance than the example I showed above. The criticism of the above inheritance is that it unnecessarily invokes the Mover constructor. Object.create creates a new object that inherits from the passed object without invoking the constructor.

    [font=courier]Cat.prototype = Object.create(Mover.prototype);[/font]

    ... but 1) that is messy as all hell ...[/quote]

    Regarding the second argument to Object.create, I don't think there's a consensus opinion among the JavaScript community, but I agree with you that the syntax seems unnecessarily verbose.

    ... 2) the value of many_braces cannot be changed from magic_number in the future ...[/quote]

    That's the default behavior, yes, but that behavior can be changed.

    <i>
    </i>Cat.prototype = Object.create(Mover.prototype, {
    many_braces: {
    value: 'magic_number',
    [b]writable: true,
    enumerable: true,
    configurable: true[/b]
    }
    });


    ... 3) this still doesn't solve the issue of having multiple constructors called.[/quote]

    Also true. You still have to do this manually with Mover.apply(this, arguments). There are several libraries that provide a custom mechanism for class definition and inheritance that fill missing features like this one, but if you're going to use just native JavaScript features, then this is what you're stuck with.
    Copy linkTweet thisAlerts:
    @svidgenApr 15.2012 — For what it's worth, you should bear in mind that this:

    [code=php]var Class = function() {
    this.doSomething = function() {}
    }[/code]


    .. is slower during object construction, but faster during method calls compared to this:

    [code=php]var Class = function() {
    }

    Class.prototype.doSomething = function() {}[/code]


    If my memory is correct, method calling decrease for each level of inheritance using the 2nd syntax.

    So, in general, if you're calling object methods more often than you're creating those objects, it's best to use the 1st syntax, attaching methods directly to the object in the constructor. This actually makes overriding and calling superclass methods [I]fairly[/I] simple as well. And I actually prefer the syntax:

    [code=php]var SubClass = function() {
    Class.call(this);

    this.anotherDoSomething = function() {}

    // override doSomething, but with some dependence on
    // the base doSomething method.
    this.baseDoSomething = doSomething;
    this.doSomething = function() {
    this.baseDoSomething();

    // now do more stuff ...
    }
    }[/code]
    Copy linkTweet thisAlerts:
    @a96authorApr 17.2012 — Thank you for the replies, especially Jeff Mott for the detailed and on target response. Here is what I've settled on (for now):
    <i>
    </i>var Base_class = Object.create(Object, {
    constructor: { value: function (argument){
    this.property = argument;
    etc();
    return this;
    }},
    method: { value: function(){
    return etc();
    }}
    });
    var Extended_class = Object.create(base_class, {
    constructor: { value: function (arg1, arg2){
    Object.getPrototypeOf(extended_class).constructor.call(this, arg1);
    this.other_property = arg2;
    return this;
    }},
    other_method: { value: function(){
    return etc();
    }}
    });
    var instance = extended_class.constructor.call(Object.create(extended_class), 1, 2);
    alert(instance.property);
    alert(instance.other_property);
    alert(instance.method());
    alert(instance.other_method());
    alert(base_class.isPrototypeOf(extended_class)); // true


    How does this look? I may be writing code for clients pretty soon, so I'm trying to work with conventions that will be understandable for people who may have to maintain my code. In other words, I don't want to look like I haven't touched ECMAscript since back in 2005.
    Copy linkTweet thisAlerts:
    @Jeff_MottApr 17.2012 — What's really interesting about this implementation is that it's more prototypal than most JavaScript code. Most JavaScript uses constructor functions to make a prototypal language appear as if it were classical. I think embracing JavaScript's prototypal nature like you've done is a step in the right direction.

    Object instantiation, though, seems a bit verbose. We could probably clean that up with a couple helper methods on the base class.

    <i>
    </i>var Base_class = Object.create(Object.prototype, {
    [color=blue]extend: { value: function (descriptors) {
    return Object.create(this, descriptors);
    }},

    <i> </i>create: { value: function () {
    <i> </i> var instance = Object.create(this);
    <i> </i> instance.constructor.apply(instance, arguments);

    <i> </i> return instance;
    <i> </i>}},[/color]

    <i> </i>constructor: { value: function (argument){
    <i> </i> this.property = argument;
    <i> </i> etc();
    <i> </i>}},

    <i> </i>method: { value: function(){
    <i> </i> return etc();
    <i> </i>}}
    });


    Then the code we write could look like this:

    <i>
    </i>var Extended_class = [color=blue]Base_class.extend([/color]{
    constructor: { value: function (arg1, arg2){
    Object.getPrototypeOf(Extended_class).constructor.call(this, arg1);
    this.other_property = arg2;
    return this;
    }},
    other_method: { value: function(){
    return etc();
    }}
    });

    var instance = [color=blue]Extended_class.create[/color](1, 2);
    alert(instance.property);
    alert(instance.other_property);
    alert(instance.method());
    alert(instance.other_method());
    alert(base_class.isPrototypeOf(Extended_class)); // true


    But there are some drawbacks to be aware of. First and foremost, Object.create wasn't implemented in IE until version 9. Depending on what browsers you need to support, this could be a deal breaker.

    Also, even though renowned developers such as Doug Crockford advocate the prototypal style, the pseudo-classical style is nonetheless more common. You'll need to provide plenty of documenting comments and examples so that other developers can easily understand how this code works and how they're supposed to use it.
    Copy linkTweet thisAlerts:
    @a96authorApr 18.2012 — > But there are some drawbacks to be aware of. First and foremost, Object.create wasn't implemented in IE until version 9. Depending on what browsers you need to support, this could be a deal breaker.

    Ah, yes. This is why I stopped doing web development. I got lured back into it with the promise that client sniffing was a thing of the past.

    According to a quick search, it seems the market share of browsers not supporting Object.create is around 40%. Also, HTML5 canvas isn't supported by those same browsers. In order to do anything, it looks like I'll have to include a lot of shims and workarounds:

    - https://github.com/kriskowal/es5-shim/blob/master/es5-shim.js#L1

    - http://code.google.com/p/explorercanvas/source/browse/trunk/silverlight/excanvas.js?r=48

    - etc.

    It looks like the updates that got me back into the game create a language that won't be usable until another decade has gone by. /:

    Is this the general state of things? How do developers generally solve the dilemma of "program to the lowest common set of features" or "program using new features for 20% of clients"? Is there a set of 20 files that I can include in the head of every html document in order to bring my potential viewers up to around N% of the web? (where N is a reasonably high number.)
    ×

    Success!

    Help @a96 spread the word by sharing this article on Twitter...

    Tweet This
    Sign in
    Forgot password?
    Sign in with TwitchSign in with GithubCreate Account
    about: ({
    version: 0.1.9 BETA 6.2,
    whats_new: community page,
    up_next: more Davinci•003 tasks,
    coming_soon: events calendar,
    social: @webDeveloperHQ
    });

    legal: ({
    terms: of use,
    privacy: policy
    });
    changelog: (
    version: 0.1.9,
    notes: added community page

    version: 0.1.8,
    notes: added Davinci•003

    version: 0.1.7,
    notes: upvote answers to bounties

    version: 0.1.6,
    notes: article editor refresh
    )...
    recent_tips: (
    tipper: @meenaratha,
    tipped: article
    amount: 1000 SATS,

    tipper: @meenaratha,
    tipped: article
    amount: 1000 SATS,

    tipper: @AriseFacilitySolutions09,
    tipped: article
    amount: 1000 SATS,
    )...