Problem using the prototype chain - javascript

If I have a constructor that takes a number of arguments:
var Mammal = function(name, weight) {
this.name = name;
this.weight = weight;
}
Mammal.prototype.makeSound = function(sound) {
alert(sound);
}
Mammal.prototype.getName = function() {
return this.name;
}
and I want to do some inheritence:
var Human = function(name,weight,language,location) {
//code
}
Human.prototype = new Mammal();
In the last line here isn't the Human prototype getting assigned undefined for the name and weight parameters? I see this code all the time....I know that the Human constructor is being fed the name and weight params but it seems messy that the prototype is getting these undefined values. I know this only works because javascript is slack enough to allow you to do this. Is there a way to get round this?

What bothers you exactly?
Human.prototype = new Mammal();
alert( Human.prototype.name ); // undefined
alert( Human.prototype.foo ); // undefined
You can consider them as not being there. The reason why you're not writing:
Human.prototype = Mammal.prototype;
Is because the Mammal constructor can add methods that are not on the prototype object.
var Mammal = function(name, weight) {
this.name = name;
this.weight = weight;
this.somefun = function() {
// this can't be inhereted by
// Mammal.prototype
}
}
In order not to reapeat yourself you can use Constructor Chaining:
var Human = function(name,weight,language,location) {
this.language = language;
this.location = location;
// call parent constructor
Mammal.apply(this, arguments);
}
This seems to be more straightforward, isn't it? You call the parent constructor to deal with the name and weight parameters, and you only care about Human specific things in the Human constructor.

i think i've seen something like this once:
var Human = function(name,weight,language,location) {
Mammal.call(this, name, weight);//calls the parent constructor
this.language = language;
this.lcoaiton = location;
}
Human.prototype = new Mammal();

Related

How to use constructors as a prototype chain?

Suppose that I have a javascript constructor:
function Person(name) {
this.name = name;
this.hello = function () { return "It's a-me, " + name + "!"; };
}
the Person "type" has a convenient method, hello that I would like to re-use on another type Student. I would like for a Student to have the following structure:
function Student(name) {
this.name = name;
this.hello = function () { return "It's a-me, " + name + "!"; };
this.books = [];
}
One option is to use the code for Student as-is above. This is sub-optimal for the usual reasons, such as that if I want it to mirror the Person type, then I have to manually keep their code in sync. Anyway, this is not good.
A second option (from this answer) is to do something like:
function Student(name) {
Person.call(this, name);
this.books = [];
}
When I mario = new Student("mario") I get the following:
Object { name: "mario", hello: hello(), books: [] }
I've successfully achieved the inheritance that I wanted, but this has the unfortunate property of placing all of the desired properties into my object. Notably, for example, there is a "hello" property on mario. It would be nice if that "hello" property could be looked up in the prototype chain.
How can I neatly create a prototype chain given the relevant object constructors?
When you create an object with new, the this value of your constructor function is set to the object, and that object's prototype is set to the prototype of the constructor Function being called.
That's why your properties are currently being added to the created object.
function Student {
this.name = name
}
const student = new Student('John')
// is (almost) equivalent to the following
const student = {}
student.name = 'John'
But if you want to add properties to the prototype instead, so that you can use inheritance, then in ES5 Javascript you can do so by assigning properties directly to the prototype of your constructor function.
function Person(name) {
this.name = name;
}
// Person is a function
// Its prototype is an instance of Object, and it has no properties
// i.e. something like Person.prototype = new Object()
Person.prototype.hello = function() {
return 'It is I, ' + this.name
}
// Now Person.prototype has one property, "hello", which is a function.
function Student(name) {
Person.call(this, name)
this.books = [];
}
// Student is a function
// Its prototype is also an instance of Object with no properties
// the following is the magic line
Student.prototype = Object.create(Person.prototype)
// We replace the prototype of the Student function with a new object, but
// Object.create() allows us to set the prototype to an existing object, in this case Person.prototype,
// Person.prototype is itself an instance of Object, and we previously
// added the "hello" function to it as a property.
const student = new Student('John')
// So what happens here?
// First a new object is created, and its prototype is set to Student.prototype
// Then we call Person.call(this)
// Which executes the body of the Person function
// So you get the properties on the object itself through the body of the Person and Student functions
// And you get the shared functionality through the prototype chain of instance -> Student.prototype -> Person.prototype -> Object.prototype
Hope that helps!
You can use prototyping method or class sugar method as you want.
Here is a simple example :
function Student(name) {
this.name = name;
this.books = [];
}
Student.prototype.hello = function(){
return "It's a-me, " + this.name + "!";
}
Student.prototype.addBook = function(book){
this.books.push(book);
}
Student.prototype.getBooks = function(){
return this.books;
}
let mario = new Student("Mario");
console.log(mario.hello());
mario.addBook("prototyping");
mario.addBook("chain");
console.log(mario.getBooks());
class Person {
constructor(name) {
this.name = name;
this.books = [];
}
hello(){
return "It's a-me, " + this.name + "!";
}
addBook(book){
this.books.push(book);
}
getBooks(){
return this.books;
}
}
let luigi = new Person("Luigi");
console.log(luigi.hello());
luigi.addBook("classSugar");
luigi.addBook("classType");
console.log(luigi.getBooks());
For longer chains use Object.assign, here is an example of making a GradStudent that is both a Student and a Person and has the personality of a Comedian and also has the properties and methods of a 4th class GameCharacter:
(function() {
//Person
function Person(name) {
this.name = name;
this.helloString = "Hello my name is "
}
Person.prototype.name = "Bob";
Person.prototype.hello = function() {
return this.helloString + this.name;
};
//Student
function Student(name, books) {
Person.call(this, name);
this.books = books;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Student.prototype.books = ["math","reading"];
//Comedian
function Comedian(name) {
Person.call(this,name);
};
Comedian.prototype = Object.create(Person.prototype);
Comedian.prototype.constructor = Comedian;
Comedian.prototype.hello = function() {
return "I don't know what my parents where thinking when they name me Squat, just kidding, my name is " + this.name;
};
//GameCharacter
function GameCharacter(power) {
this.power = power;
};
GameCharacter.prototype = new Object();
GameCharacter.prototype.constructor = GameCharacter;
GameCharacter.prototype.gainPower = function(power) {
this.power += ", "+power;
};
GameCharacter.prototype.statePower = function() {
return this.power;
};
//GradStudent
function GradStudent(name, books, degree) {
Comedian.call(this, name);
Student.call(this,name,books);
GameCharacter.call(this, "jumping");
this.degree = degree;
this.gainPower("flying");
}
GradStudent.prototype = Object.create(Student.prototype);
Object.assign(GradStudent.prototype, Comedian.prototype, GameCharacter.prototype);
GradStudent.prototype.constructor = GradStudent;
var gradStudent = new GradStudent("Bill",["C", "C++", "JavaScript"], "B.S.");
console.log(gradStudent.hello() + " I have a " + gradStudent.degree +" I am studying " + gradStudent.books.toString() + ". \n In a game I play my power's are "+ gradStudent.statePower() + ". \n Is gradStudent also a Student? " + (gradStudent instanceof Student) + "" );
var otherStudent = new Student("Jennifer" ,["english", "science"]);
console.log(gradStudent.books.toString() + " " + otherStudent.books.toString());
})();
GradStudent is an instance of Student, it's a type of Student, and can also do all the things Comedian and GameCharacter does. The value of Object.assign is that kind of multiple inheritance.
I can accomplish such a thing with the following:
function Student(name) {
Object.setPrototypeOf(this, new Person(name));
this.books = [];
}
However, I'm not familiar enough with javascript to know what possible problems might arise with this solution. Coming from other OO style languages, it feels weird for the prototype of mario to be an actual instance of a Person, but I suppose everything in js is an instance, in some sense, so this might just be bias on my part.

How can I make inheritance without third object shim?

I try to understand OOP in JavaScript.
I want to create object Person and Worker. Worker inherits Person. I wrote code:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.grow = function () {
this.age++;
console.log('Person grows, new age ' + this.age);
};
function Worker(name, age, position) {
// How to call parrent constructor Person?
this.position = position;
}
Worker.prototype = Person.prototype;
Worker.constructor = Worker.prototype.constructor;
Worker.prototype.promote = function () {
console.log('Worker ' + this.name + ' became Senior ' + this.position);
};
Worker.prototype.grow = function () {
this.promote();
// How to call parrent method grow?
};
var workerObj = new Worker('Ben', 39, 'engineer');
workerObj.grow();
But in JS variable assignment works by reference and expression Worker.prototype = Person.prototype; doesn't get result and method grow in Worker overrides method grow in Person and I will not be able to get access to parent method.
I know that I need to use third object. But is there way to inherit without third object?
Setting the functions' .prototype properties to the same object essentially makes them the same class, as you've noticed. What you really need, is for Person.prototype to be an object that inherits from Worker.prototype. But you need to do this without calling the Person constructor, since that leave some side effects back in the prototype chain (they aren't significant in the case you mention, but they could be significant in other cases). So Worker.prototype = new Person(); isn't an ideal solution for you.
Object.create() works for this. Among other things, this function can be used to create an object that inherits from another, without calling any constructors. For the situation you're talking about, it would look like this:
Worker.prototype = Object.create(Person.prototype);
Worker.prototype.constructor = Worker;
Object.create doesn't work in older versions of IE, and a total polyfill isn't available due to engine limitations. But there are polyfills for Object.create which work well enough to enable this kind of subclassing, so browser support need not be an issue.
I typically wrap this up in a function, so that once I've defined my subclass and superclass, I can express the relation without much extra rigamarole. You could do that like this:
Object.subclass = function (sub, sup) {
sub.prototype = Object.create(sup.prototype);
sub.prototype.constructor = sub;
}
Object.subclass(Worker, Person); // "class Worker extends Person"
I make inheritance in javascript like this :
var c_Parent = function () {
};
var c_Child = function () {
c_Parent .call(this);
};
c_Child.prototype = new c_Parent();
c_Child.prototype.constructor = c_Child;
So in you case :
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.grow = function () {
this.age++;
console.log('Person grows, new age ' + this.age);
};
function Worker(name, age, position) {
this.position = position;
Person.call(this, name, age);
}
Worker.prototype = new Person();
Worker.prototype.constructor = Worker;
Worker.prototype.promote = function () {
console.log('Worker ' + this.name + ' became Senior ' + this.position);
};
Worker.prototype.grow = function () {
this.promote();
Person.prototype.grow.call(this);
};
var workerObj = new Worker('Ben', 39, 'engineer');
workerObj.grow();

Javascript: the variable from [user-defined prototype] is not available

I play javascript with the book Professional JavaScript for Web Developers. I practice an example in section 6.2.6. the codes are listed below:
function creatPrototype(subType, superType)
{
function subTypePrototype(){};
subTypePrototype.prototype = superType.prototype;
subTypePrototype.constructor = subType;
subTypePrototype.str = "say";
return new subTypePrototype();
}
function Person(name, age)
{
this.name = name;
this.age = age;
}
Person.prototype.say = function(){
writeln("bill say");
}
function itMan(name, age){
Person.apply(this, arguments);
}
itMan.prototype = creatPrototype(itMan, Person);
var Bill = new itMan("bill", 25);
writeln(itMan.prototype.str); //expect "say"
writeln(Person.prototype == itMan.prototype.prototype); //expect true
Bill.say(); //expect "bill say"
the result is:
undefined
False
bill say
Why?
itMan.prototype.str is suppose to "say"
Person.prototype AND itMan.prototype.prototype should point to a same object
Bill.say() run correctly, so the prototype chain is OK.
You have to think about which property belongs to the constructor function and which one belongs to the instance. prototype is a property of the function, but constructor and str should be both properties of the instance.
This should do it:
function createPrototype(subType, superType)
{
function subTypePrototype(){};
subTypePrototype.prototype = superType.prototype;
var newPrototype = new subTypePrototype();
newPrototype.constructor = subType;
newPrototype.str = "say";
return newPrototype;
}
But, as you are also passong subType, you can actually assign the prototype directly:
function inherit(subType, superType)
{
function tconstr(){};
tconstr.prototype = superType.prototype;
subType.prototype = new tconstr();
subType.prototype.constructor = subType;
subType.prototype.str = "say";
}
and then just call it with
inherits(itMan, Person);
Person.prototype AND itMan.prototype.prototype should point to a same object
Remember that prototype is a property of a function, not of objects. But itMan.prototype is a an object. You cannot access an objects prototype unless you explicitly refer to it (but I would not do so).
With ECMAScript 5, there is a way to get the prototype, using Object.getPrototypeOf [MDN]. This only works in newer browsers though.
Here is a working example of your code.
There were some mistake with the code, try this code
function creatPrototype(subType, superType)
{
function subTypePrototype(){
this.prototype = superType.prototype;
this.constructor = subType;
this.str = "say";
}
return new subTypePrototype();
}
function Person(name, age)
{
this.name = name;
this.age = age;
}
Person.prototype.say = function(){
document.writeln("bill say");
}
function itMan(name, age){
Person.apply(this, arguments);
}
itMan.prototype = creatPrototype(itMan, Person);
var Bill = new itMan("bill", 25);
document.writeln(itMan.prototype.str); //expect "say"
document.writeln(Person.prototype == itMan.prototype.prototype); //expect true
Bill.prototype.say(); //expect "bill say"
In your code, you were not using this object so itMAn had no variable str,
you were using
subTypePrototype.constructor = subType;
and subTypePrototype.prototype = superType.prototype;
therefore Bill.say was working and Person.prototype == itMan.prototype.prototype was not working.
Prototype is a reserved keyword and you have to be very careful while using it
Articles on prototype keyword
You have several issues in your code.
Then within your createSubPrototype :
return new subTypePrototype();
You are creating a function and then you are returning the executed function result where you should return a pointer to your function like
return subTypePrototype;
But you should not create a function cause it seems you want to retrieve parent "prototype" :)
So indeed your code should look more like :
function createPrototype(subType, superType)
{
subTypePrototype = superType.prototype;
subTypePrototype.constructor = subType; //- beware this is wrong !!!
subTypePrototype.str = "say";
return subTypePrototype;
}
Check the line I have marked as wrong, by doing so you are updating both sub & parent type
How to do it :
That being said if you want to extend an object I would advise you to use existing libs. (jQuery, Mootools, etc ). Here a sample on how to do it properly

how do i namespace pseudo-classical javascript

I have some simple OO code I've written that I'm playing with:
//define a constructor function
function person(name, sex) {
this.name = name;
this.sex = sex;
}
//now define some instance methods
person.prototype.returnName = function() {
alert(this.name);
}
person.prototype.returnSex = function() {
return this.sex;
}
person.prototype.talk = function(sentence) {
return this.name + ' says ' + sentence;
}
//another constructor
function worker(name, sex, job, skills) {
this.name = name;
this.sex = sex;
this.job = job;
this.skills = skills;
}
//now for some inheritance - inherit only the reusable methods in the person prototype
//Use a temporary constructor to stop any child overwriting the parent prototype
var f = function() {};
f.prototype = person.prototype;
worker.prototype = new f();
worker.prototype.constructor = worker;
var person = new person('james', 'male');
person.returnName();
var hrTeamMember = new worker('kate', 'female', 'human resources', 'talking');
hrTeamMember.returnName();
alert(hrTeamMember.talk('I like to take a lot'));
Now this is all well and good. But I'm confused. I want to include namespacing as part of my code writing practice. How can I namespace the above code. As it is now I have 2 functions defined in the global namespace.
The only way I can think to do this is to switch to object literal syntax. But then how do I implement the pseudo-classical style above with object literals.
You could for example do following:
var YourObject;
if (!YourObject) {
YourObject = {};
YourObject.Person = function(name, sex) {
// ...
}
YourObject.Person.prototype.returnName = function() {
// ...
}
// ...
}
You don't have to use object literals, at least, not exclusively.
Decide on the single global symbol you'd like to use.
Do all your declaration work in an anonymous function, and explicitly attach "public" methods as desired to your global object:
(function(global) {
// all that stuff
global.elduderino = {};
global.elduderino.person = person;
global.elduderino.worker = worker;
})(this);
I may not be completely understanding the nuances of your issue here, but the point I'm trying to make is that Javascript makes it possible for you to start with your symbols being "hidden" as locals in a function, but they can be selectively "exported" in various ways.

Popular JavaScript Inheritance Patterns

I'm working on an ebook on GitHub on TDD JavaScript and I'm wondering if I'm missing any popular inheritance patterns. If you know of any additional patterns I'd love to see them. They should have the following:
Time tested - used in real apps
Source code should be supplied. Should be as straight forward and pedantic as possible.
Of course be correct and working.
The reason I'm doing this is that it seems that object inheritance in JavaScript has been quite difficult for many of us to grok. my JavaScript inheritance chapter is basically a study aid to: Crockford's Good Parts and Zakas's Professional JavaScript for Web Developers.
Here are the patterns I have so far:
// Pseudoclassical Inheritance
function Animal(name) {
this.name = name;
this.arr = [1,2,3];
};
Animal.prototype = {
constructor: Animal,
whoAmI: function() { return "I am " + this.name + "!\n"; }
};
function Dog(name, breed) {
this.name = name;
this.breed = breed;
};
Dog.prototype = new Animal();
Dog.prototype.getBreed = function() {
return this.breed;
};
Dog.prototype.bark = function() {
return 'ruff ruff';
};
// Combination Inheritance
function Parent(name) {
this.name = name;
this.arr = [1,2,3];
};
Parent.prototype = {
constructor: Parent,
toString: function() { return "My name is " + this.name; }
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
};
Child.prototype = new Parent();
Child.prototype.getAge = function() {
return this.age;
};
// Prototypal Inheritance
var helper = { // Thanks to Bob Vince for reminding me NOT to clobber Object!
inherit: function(p) {
NewObj = function(){};
NewObj.prototype = p;
return new NewObj();
},
inheritPrototype: function(subType, superType) {
var prototype = helper.inherit(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
};
function SubType(name, age) {
Parent.call(this, name);
this.age = age;
};
//Child.prototype = new Parent(); // Gets replaced by:
helper.inheritPrototype(SubType, Parent);
SubType.prototype.getAge = function() {
return this.age;
};
// Functional - Durable Pattern
function super_func(blueprint) {
var obj = {};
obj.getName = function() { return blueprint.name; };
obj.getAge = function() { return blueprint.age; };
obj.getFoo = function() { return blueprint.foo; };
obj.getBar = function() { return blueprint.bar; };
return obj;
};
function sub_func(blueprint) {
blueprint.name = blueprint.name || "Crockford's Place";
supr = super_func(blueprint);
supr.coolAugment = function() { return "I give a fresh new perspective on things!" };
return supr;
};
And for those interested, here are the jspec tests (sorry but Markdown or whatever they're using mangles the format a bit):
describe 'JavaScript Inheritance Tests'
before_each
animal = new Animal("Onyx")
dog = new Dog("Sebastian", "Lab")
person = { password : 'secret', toString : function(){ return '<Person>' } }
stub(person, 'toString').and_return('Original toString method!')
end
describe 'Pseudoclassical Inheritance Creation'
it 'should create parent and child object using pseudoclassical inheritance'
animal.constructor.should.eql Animal
// dog.constructor.should.eql Dog // Nope: expected Animal to eql Dog
dog.constructor.should.eql Animal
animal.should.be_a Animal
dog.should.be_a Animal
// dog.should.be_a Dog // Nope! We severed the original prototype pointer and now point to Animal!
dog.should.be_an_instance_of Animal
dog.should.be_an_instance_of Dog
(animal instanceof Dog).should.be_false
end
it 'should behave such that child inherits methods and instance variables defined in parent'
animal.whoAmI().should.match /I am Onyx.*/
dog.whoAmI().should.match /Sebastian.*/
animal.should.respond_to 'whoAmI'
dog.should.respond_to 'whoAmI'
dog.should.have_prop 'name'
end
it 'should behave such that methods and instance variables added to child are NOT available to parent'
dog.bark().should.match /Ruff Ruff/i
dog.should.have_property 'breed'
dog.should.respond_to 'bark'
// animal.should.have_prop 'breed' // Of course not!
// animal.should.respond_to 'bark' // Of course not!
end
it 'should behave such that reference variables on the parent are "staticy" to all child instances'
dog.arr.should.eql([1,2,3])
dog.arr.push(4)
dog.arr.should.eql([1,2,3,4])
spike = new Dog("Spike", "Pitbull")
spike.arr.should.eql([1,2,3,4])
spike.arr.push(5)
rover = new Dog("Rover", "German Sheppard")
spike.arr.should.eql([1,2,3,4,5])
rover.arr.should.eql([1,2,3,4,5])
dog.arr.should.eql([1,2,3,4,5])
end
end
describe 'Combination Inheritance Solves Static Prototype Properties Issue'
it 'should maintain separate state for each child object'
child_1 = new Child("David", 21)
child_2 = new Child("Peter", 32)
child_1.arr.push(999)
child_2.arr.push(333)
child_1.arr.should.eql([1,2,3,999])
child_2.arr.should.eql([1,2,3,333])
child_1.getAge().should.eql 21
child_1.should.be_a Parent
end
end
describe 'Prototypal Inheritance'
it 'should inherit properties from parent'
person.toString().should.match /Original toString.*/i
person.password.should.eql 'secret'
joe = helper.inherit(person)
joe.password.should.eql 'secret'
joe.password = 'letmein'
joe.password.should.eql 'letmein'
person.password.should.eql 'secret'
end
end
describe 'Parisitic Combination Inheritance'
it 'should use inheritPrototype (to call parent constructor once) and still work as expected'
sub = new SubType("Nicholas Zakas", 29)
sub.toString().should.match /.*Nicholas Zakas/
sub.getAge().should.eql 29
charlie = new SubType("Charlie Brown", 69)
charlie.arr.should.eql([1,2,3])
charlie.arr.push(999)
charlie.arr.should.eql([1,2,3,999])
sub.arr.should.eql([1,2,3])
sub.should.be_an_instance_of SubType
charlie.should.be_an_instance_of SubType
(sub instanceof SubType).should.eql true
(sub instanceof Parent).should.eql true
end
end
describe 'Functional Durable Inheritance'
it 'should hide private variables'
sup = new super_func( {name: "Superfly Douglas", age: 39, foo: "foo", bar: "bar"} )
sup.getName().should.eql 'Superfly Douglas'
sup.name.should.be_undefined
sup.getAge().should.eql 39
sup.age.should.be_undefined
sup.getFoo().should.eql 'foo'
sup.foo.should.be_undefined
end
it 'should create a descendent object that inherits properties while maintaining privacy'
sub = new sub_func( {name: "Submarine", age: 1, foo: "food", bar: "barfly"} )
sub.getName().should.eql 'Submarine'
sub.name.should.be_undefined
sub.getAge().should.eql 1
sub.age.should.be_undefined
sub.getFoo().should.eql 'food'
sub.foo.should.be_undefined
sub.getBar().should.eql 'barfly'
sub.bar.should.be_undefined
sub.coolAugment().should.match /.*fresh new perspective.*/
//sub.should.be_an_instance_of super_func NOPE!
//sub.should.be_an_instance_of sub_func NOPE!
sub.should.be_an_instance_of Object
end
end
end
Thanks all! Oh, and if you want to check out my essay/book I'd love to get feedback:
TDD JavaScript at GitHub repo
See How to "properly" create a custom object in JavaScript? for a summary. (Might as well link it, since I wasted so much time typing it out!)
this:
Dog.prototype = new Animal();
would generally be avoided. You see it in example/tutorial code, but it's a horrible mess because it's basing a class on an instance, and an instance constructed in a faulty way: name is undefined. Any more complicated constructor is going to get upset at that sort of thing.
Object.prototype.inherit=
Is a better approach for constructing, but prototyping anything into Object is considered very poor taste. It runs the risk of messing up use of objects as trivial maps and breaking other code. You can put this helper function elsewhere, eg. Function.prototype.subclass.
prototype.constructor
Personally I would tend to avoid, because constructor has a special meaning in JavaScript (as implemented in Firefox and some other browsers; not IE's JScript), and that meaning is not what constructor does here nor what you would expect any such property to do; it's confusing and almost always best avoided. So if including a link to the constructor function in the instance in a class system I would prefer to name it something else.
A co-worker at my previous company developed a library to do java like inheritance http://www.uselesspickles.com/class_library/. I think it's sexier than Rajendra's suggestions, syntax looks cleaner.
I wrote an article that demonstrates different ways to approach it, but making sure that the known bad practices are avoided. http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html, this is if you don't want to download a library but just want to copy paste some code that you can improve to do what you need.
There's an interesting pattern worth mentioning here: a JavaScript constructor may return any object (not necesserily this). One could create a constructor function, that returns a proxy object, that contains proxy methods to the "real" methods of the "real" instance object. This may sound complicated, but it is not; here is a code snippet:
var MyClass = function() {
var instanceObj = this;
var proxyObj = {
myPublicMethod: function() {
return instanceObj.myPublicMethod.apply(instanceObj, arguments);
}
}
return proxyObj;
};
MyClass.prototype = {
_myPrivateMethod: function() {
...
},
myPublicMethod: function() {
...
}
};
The nice thing is that the proxy creation can be automated, if we define a convention for naming the protected methods. I created a little library that does exactly this: http://idya.github.com/oolib/
I've got at least half a dozen implementations of various inheritance patterns lying around in my dev/web/stuff folder, but they are mostly toys.
What I actually sometimes use is the following thin wrapper over JavaScript's default pseudo-class-based approach to make inheritance easier:
Function.prototype.derive = (function() {
function Dummy() {}
return function() {
Dummy.prototype = this.prototype;
return new Dummy;
};
})();
Example code:
function Pet(owner, type, name) {
this.owner = owner;
this.type = type;
this.name = name;
}
Pet.prototype.toString = function() {
return this.owner + '\'s ' + this.type + ' ' + this.name;
};
function Cat(owner, name) {
Pet.call(this, owner, 'cat', name);
}
Cat.prototype = Pet.derive();
var souris = new Cat('Christoph', 'Souris');
Another interesting one is the following, which automatically adds factory methods to a proper prototypal approach:
var Proto = new (function() {
function Dummy() {}
this.clone = function() {
Dummy.prototype = this;
return new Dummy;
};
this.init = function() {};
this.create = function() {
var obj = this.clone();
this.init.apply(obj, arguments);
return obj;
};
});
Example code:
var Pet = Proto.clone();
Pet.init = function(owner, type, name) {
this.owner = owner;
this.type = type;
this.name = name;
};
Pet.toString = function() {
return this.owner + '\'s ' + this.type + ' ' + this.name;
};
Cat = Pet.clone();
Cat.init = function(owner, name) {
Pet.init.call(this, owner, 'cat', name);
};
// use factory method
var filou = Cat.create('Christoph', 'Filou');
// use cloning (the proper prototypal approach)
var red = filou.clone();
red.name = 'Red';
You've already seen my implementation of classes.
Late to the party here but I have 2 points to make.
1) Please do not inform people to inherit through creating supertype objects. This is considered bad practice for a few reason. Firstly, its a principle mistake. You are instantiating objects just to use their methods and not doing anything with the instance per se. The right way to have done this is to use the Object.prototype.inherit method. In addition, this method forces you to leave the supertype constructor function argument empty, which may invoke an error under strict circumstances.
2) You forgot to mention the constructor stealing pattern.
function Supertype(name){
this.name=name;
this.sayName = function(){console.log(this.name);};
}
function Subtype(name){
//inherit by using (stealing) supertype constructor function
Supertype(name);
// child specific properties
//
}

Categories

Resources