Viktor
Viktor

Reputation: 507

Grasping "OOP" JavaScript: module pattern with inheritance

I'm trying to grasp the module pattern with added inheritance. I come from a university background, with mostly Java in my trunk, but I have been working with web techniques for about ten years. I'm only about a year into JavaScript though...

Anyway, I'm trying a simple inheritance (.prototype) example. From a People object, you can add Gentleman's and then list them using their .toString() method. A Gentleman is a child to a Human. It went good until I implemented the "abstract" Human, but now the code will not run.

Please, comment on what is considered bad with my code. I would like to stay with the module/prototype approach though, but what am I doing wrong? I would also listen to what this means in different contexts. I.e., in People I can use the private _people directly, but in submodules I have to use this._name--why?

var People = People || {};

People = (function() {
    var People = function(){
        this._people = [];
    };

    var addGentleman = function (name) {
        this._people.push(new People.Gentleman(name));
    };

    var getList = function () {
        var temp = [];

        for (var i = 0; i < this._people.length; i++) {
            temp.push(this._people[i].toString());
        }

        return temp;
    };

    People.prototype.constructor = People;
    People.prototype.addGentleman = addGentleman;
    People.prototype.getList = getList;

    return People;
})();

People.Human = (function () {
    var Human = function (name, hasLadyParts) {
        this._name = name;
        this._hasLadyParts = hasLadyParts;
    };

    var hasLadyParts = function () {
        return this._hasLadyParts;
    };

    var toString = function () {
        var str = this._name;
        if (!this._hasLadyParts) str += ' no';
        return str + ' lady parts.';
    };

    Human.prototype.constructor = Human;
    Human.prototype.hasLadyParts = hasLadyParts;
    Human.prototype.toString = toString;

    return Human;
})();

People.Gentleman = (function () {
    var Gentleman = function (name) {
        People.Human.call(this, name, false);
    }

    var toString = function () {
        return 'Mr.' + People.Human.toString();
    };

    // Gentleman.prototype = Object.create(People.Human.prototype);
    Gentleman.prototype.constructor = Gentleman;
    Gentleman.prototype.toString = toString;

    return Gentleman;
})();

$(function () {
    var people = new People();
    people.addGentleman('Viktor');
    people.addGentleman('Joakim');
    var list = people.getList();
    var $ul = $('#people');

    for (var i = 0; i < list.length; i++) {
        $ul.append('<li>' + list[i] + '</li>');
    }
});

Fiddle: http://jsfiddle.net/5CmMd/5/

Edit: I've updated code and fiddle a bit. If I get this working, I think I understand most of the design. This example would also work as a simple tutorial for future OOP programmers visiting JavaScript land, I think.

Upvotes: 0

Views: 1060

Answers (2)

a better oliver
a better oliver

Reputation: 26858

Gentleman.prototype = Object.create(People.Human.prototype);

Gentleman.prototype = {
    constructor = Gentleman,
    toString = toString
};

Should be constructor: Gentleman, .... Moreover you assign to the prototype twice and therefore overwrite it. That has the side effect that Gentleman no longer inherits from Human. You have to add to it:

Gentleman.prototype.toString = toString;

Or you add the properties with the Object.create() call. see reference

As for this:

You can use _peopledirectly because it is a variable. All instances of Peopleshare the same list of people.

When you call a function as a method of an object this refers to the object itself. Since every Human should have its own name this._name refers to the name of this ^^ human. So aGentleman.toString()would return the name of exactly this gentleman.

People.Human = (function () {
  this._name; //<- does not do anything.

Upvotes: 2

Tom Elmore
Tom Elmore

Reputation: 1980

I think the main issue is in the prototype of Gentlemen. For one you are overwriting the prototype youve inherited from Human, for another you are using = instead of : to assign the functions :)

Try this:

    var People = People || {};

People = (function() {
    var _people = [];

    var People = function(){};

    var addGentleman = function (name) {
        _people.push(new People.Gentleman(name));
    };

    var getList = function () {
        var temp = [];

        for (var i = 0; i < _people.length; i++) {
            temp.push(_people[i].toString());
        }

        return temp;
    };

    People.prototype = {
        constructor: People,
        addGentleman: addGentleman,
        getList: getList
    };

    return People;
})();

People.Human = (function () {
    this._name;

    var Human = function (name) {
        this._name = name;
    };



    Human.prototype = {
        constructor: Human,
    };

    return Human;
})();

People.Gentleman = (function () {

    var Gentleman = function (name) {
        People.Human.call(this, name);     
    }

    var toString = function () {
        return 'Mr. ' + this._name;
    };

   Gentleman.prototype = Object.create(People.Human.prototype);


    Gentleman.prototype.constructor = Gentleman;
    Gentleman.prototype.toString = toString;

    return Gentleman;
})();

$(function () {
    var people = new People();
    people.addGentleman('Viktor'); // this is me
    people.addGentleman('Joakim'); // and my friend!
    var list = people.getList();
    var $ul = $('#people');

    for (var i = 0; i < list.length; i++) {
        $ul.append('<li>' + list[i] + '</li>');
    }
});

You can see that I have added the new toString method to the prototype object rather than overwriting whats already there. I dont know if the pattern has a nicer way to do this (im not familiar with it).

You could do this if it seems more appropriate:

Gentleman.prototype = Object.create(People.Human.prototype, 
{ 
    constructor : { configurable: true, get : function() { return Gentleman } }, 
    toString : { configurable: true, get : function() { return toString } }
});

Upvotes: 1

Related Questions