2
\$\begingroup\$

Are there any potential issues with the following Javascript code:

var BobsGarage = BobsGarage || {}; // namespace

/**
 * BobsGarage.Car
 * @constructor
 * @returns {BobsGarage.Car}
 */
BobsGarage.Car = function() {

    /**
     * Engine
     * @constructor
     * @returns {Engine}
     */
    var Engine = function() {
        // definition of an engine
    };

    Engine.prototype.constructor = Engine;
    Engine.prototype.start = function() {
        console.log('start engine');
    };

    /**
     * Tank
     * @constructor
     * @returns {Tank}
     */
    var Tank = function() {
        // definition of a tank
    };

    Tank.prototype.constructor = Tank;
    Tank.prototype.fill = function() {
        console.log('fill tank');
    };

    this.engine = new Engine();
    this.tank = new Tank();
};

BobsGarage.Car.prototype.constructor = BobsGarage.Car;

/**
 * BobsGarage.Ferrari
 * Derived from BobsGarage.Car
 * @constructor
 * @returns {BobsGarage.Ferrari}
 */
BobsGarage.Ferrari = function() {
    BobsGarage.Car.call(this);
};
BobsGarage.Ferrari.prototype = Object.create(BobsGarage.Car.prototype);
BobsGarage.Ferrari.prototype.constructor = BobsGarage.Ferrari;
BobsGarage.Ferrari.prototype.speedUp = function() {
    console.log('speed up');
};

// Test it on the road

var car = new BobsGarage.Car();
car.tank.fill();
car.engine.start();

var ferrari = new BobsGarage.Ferrari();
ferrari.tank.fill();
ferrari.engine.start();
ferrari.speedUp();

// var engine = new Engine(); // ReferenceError

console.log(ferrari);

The goal is to have prototype inheritance and nested classes so that classes defined within BobsGarage.Car are not accessible outside the constructor of BobsGarage.Car but instances of them are accessible to derived classes, as shown in the test code.

Note: I am referring to the concept of Class in Javascript as defined on the MDN.

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

I don't see any issues with your code.

One suggestion would be to make your code a little DRYer by assigning to your prototype in 1 go and drop the jsdoc ( it adds a lot of lines, for little value ).

//Definition of an engine
var Engine = function() {};

Engine.prototype = {
  constructor : Engine,
  start : function() {
    console.log('start engine');
  }
};

Instead of

/**
 * Engine
 * @constructor
 * @returns {Engine}
 */
var Engine = function() {
    // definition of an engine
};

Engine.prototype.constructor = Engine;
Engine.prototype.start = function() {
    console.log('start engine');
};

Creating ferrari out of car is even more verbose, I would suggest 2 helper functions to keep the code more readable.

BobsGarage.Ferrari.prototype = {
  constructor : BobsGarage.Ferrari,
  speedUp : function() {
    console.log('speed up');
  },
}

extendPrototype( BobsGarage.Car , BobsGarage.Ferrari );

function extendPrototype( base , extension )
{
  extension.prototype = extend( base.prototype , extension.prototype );
}

function extend( o , extension )
{
  for( var key in extension )
    o[key] = extension[key];
  return o;
}

Obviously you would keep re-using extend and extendPrototype.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Vary good suggestion, thanks. The only thing I think needs to be changed is the implementation of extendPrototype. The way it is implemented now also extends the base prototype, effectively assigning all extension properties to the base prototype. This can be avoided creating a new temporary object from the base prototype like this: var klass = function() {}; klass.prototype = new base(); extension.prototype = extend(klass.prototype, extension.prototype);. \$\endgroup\$ Commented Jan 4, 2014 at 10:54
  • \$\begingroup\$ You are right, not sure how that got past my test ;) \$\endgroup\$
    – konijn
    Commented Jan 4, 2014 at 15:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.