HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptModerate

Creature generator

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
creaturegeneratorstackoverflow

Problem

Originally posted this here, and it was helpfully suggested to post in this forum.

In the past couple of years I've returned part time to programming after a 15 year gap. I was C/UNIX. So, I've picked up PHP, Java and C++ ok, but have struggled with JavaScript.

Finally I think I've found a way to 'create' classes that can inherit and wondered if anyone would care to comment. Here is an example:


  Basic

  
  

function Base( options ) {
    var that = this;
    options = options || {};
    Object.keys( options ).forEach( function( item ) {
        that[item] = options[item];
    });
}

function Creature( options ) {
    this.legs = 4;

    Base.call( this, options );
    console.log("New creature");
}

Creature.prototype.showNumberOfLegs = function() {
    console.log( "Number legs " + this.legs );
};

function Mammal( options ) {
    this.fur = true;
    Creature.call( this, options );

    console.log("New mammal");
}

Mammal.prototype = Object.create( Creature.prototype );

Mammal.prototype.showFur = function() {
    console.log( "Fur " + this.fur );
};

var c = new Creature();
c.showNumberOfLegs();

var m = new Mammal({ legs: 6, fur: false });
m.showNumberOfLegs();
m.showFur();
  

Solution

but have struggled with JavaScript

Welcome to JavaScript, where all things look fine but are actually half broken. :D


Finally I think I've found a way to 'create' classes that can inherit

I think there's a saying that goes like "Composition over inheritance". That's because composition is more flexible and doesn't impose hard taxonomies of classes or make you resort to multiple inheritance. See this video for a detailed comparison.

Now let's go over to your code. Let's just say for now that I advocate inheritance. Let's do this!

options = options || {};
Object.keys( options ).forEach( function( item ) {
    that[item] = options[item];
});

// to

Object.keys(options || {}).forEach(function(key){
  this[key] = options[key];
}, this);

// to

Object.assign(this, options || {});


You can streamline your assignment operation by inlining the defaulting of options to an empty object. The context (this) can also be provided to the callback of forEach via its second argument.

If you can do ES6, there's Object.assign() which does the same thing in lesser code. It pretty much looks like jQuery's $.extend if you're familiar with it.

Base.call( this, options );

// to

Base.apply(this, arguments);


The reason is that you don't actually know how many arguments you'll potentially provide the constructor. In your code, you assume your only argument is options, but that might change. apply allows you to pass in an array or array-like object as an argument, spreading it as the arguments on the receiving end.

You are missing:

Creature.prototype = Object.create(Base.prototype);


Here's a checklist of what to do when doing prototypal inheritance manually:

  • Inherit the parent properties (Parent.apply(this, arguments).



  • Inherit the parent prototype (Child.prototype = Object.create(Parent.prototype))



  • Define child properties and their default values



  • Define child methods

Code Snippets

options = options || {};
Object.keys( options ).forEach( function( item ) {
    that[item] = options[item];
});

// to

Object.keys(options || {}).forEach(function(key){
  this[key] = options[key];
}, this);

// to

Object.assign(this, options || {});
Base.call( this, options );

// to

Base.apply(this, arguments);
Creature.prototype = Object.create(Base.prototype);

Context

StackExchange Code Review Q#106541, answer score: 11

Revisions (0)

No revisions yet.