I'm trying to apply prototyped inheritance to a function in Javascript. It's all pretty plain and even described in Wikipedia's javascript lemma. It works if my properties are simple javascript types:
function Person() {
this.age = 0;
this.location = {
x: 0,
y: 0,
absolute: false
};
};
function Employee() {};
Employee.prototype = new Person();
Employee.prototype.celebrate = function () {
this.age++;
}
var pete = new Employee();
pete.age = 5;
pete.celebrate();
var bob = new Employee();
bob.celebrate();
console.log("bob is " + bob.age + " pete is " + pete.age);
With Employee.prototype = new Person();, all Person's properties and (prototyped) methods are inherited by Employee, which is fundamental to inheritance.
This works as expected: bob is 1 pete is 6
Now I'm starting to fiddle with pete's location (after celebrating)
pete.celebrate();
pete.location.absolute=true;
Displaying bob.location.absolute shows: true, which is contra intuitive (I didn't touch bob's location so I expect it to have the initial value declared in Person) and ruins my solution.
In my initial understanding this should have been false. I do realize that I probably should clone the location object from the initial Person, but I'm not sure where or how to do this. And if there are maybe better techniques for inheritance?
3 Answers 3
When you instantiate a new Employee, all properties of Person are copied. As this is a shallow copy, pete and bob share the same location object. For your problem, there does not seems a very good solution. You can either use a framework or do a hack like this:
function Employee() { Person.apply(this); };
This calls the Person constructor in the context of the this object.
The MDC has more info on this: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/apply
Comments
Don't run the Person constructor when inheriting, an Employee shouldn't
even have a .location because it's not in Person.prototype.
function createObject( fn ){
function f(){}
f.prototype = fn.prototype;
return new f;
}
Then:
Employee.prototype = createObject( Person );
This will inherit properly without side effects (running constructor).
You would only run the parent constructor in the child constructor:
function Employee() {
Person.apply( this, arguments );
}
4 Comments
new Employee will still be instanceof Person because both Person.prototype and f.prototype point to the same object..__proto__ or Object.create for this but they are not supported in every browser.I ran into similar issue. I ended up using a separate constructor for the internal object.
function engine(cc, fuel) {
this.cc = cc;
this.fuel = fuel
}
function car(type, model, cc, fuel) {
this.type = type;
this.model = model;
this.engine = new engine(cc, fuel);
}
var mycar = new car("sedan", "toyota corolla", 1500, "gas");
console.log(mycar.type);
//sedan
console.log(mycar.engine.cc);
//1500
If I had any methods on the prototypes of 'engine' or 'car' constructors, they would still be available. However, I am not deriving the "car" class from the "engine" class in OOP sense. I didn't need to. The "engine" is used as a component of "car".
Meanwhile, for inheritance, I prefer to use the new methods incorporated in ECMAScript 5 which means using Object.create, Object.defineProperties, etc. They are supported even in IE9. Before that I used the same 'apply()' method suggested by Kosta.