Reputation: 507
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
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 _people
directly because it is a variable. All instances of People
share 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
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