Reputation: 5571
I have this simple inheritance pattern:
function Emitter() {
this.dis1 = this;
};
function Person() {
this.dis2 = this;
};
Person.prototype = new Emitter();
var p1 = new Person();
var p2 = new Person();
console.log(p1.dis1 === p2.dis1); //true - why?
console.log(p1.dis2 === p2.dis2); //false
Why are dis1 of separate objects the same? Is there a workaround for this?
Upvotes: 4
Views: 74
Reputation: 27853
A note on how the new
operator works:
Foo = function(){};
Foo.prototype;
var bar = new Foo();
What happens on the last line is a new object is created which inherits from Foo.prototype
and then the code in the Foo
function is executed with this
being that new object. Then bar is initialized to that new object.
Your inheritance model has several flaws and you have uncovered one of them. All the flaws stem from this line of code:
Person.prototype = new Emitter();
This line says that the object all Person instances inherit from is an instance of Emitter (the instance of Emitter created there).
p1.dist1
looks up the dist1 property on the prototype chain. First it looks in p1
to see if it has the dist1
property and it does not. Next it looks in p1.[[Prototype]]
which was initialized to Person.prototype
where it does find the dist1
property and it's value is Person.prototype
(the one instance of Emitter mentioned above).
p1.dist
does the same thing, but the property is found in p1
and it is equal to p1
.
The same thing happens with p2
, p2.dist1
being the same instance of Emitter while p2.dist2
being equal to p2
.
A way to do inheritance and avoid all these problems would be:
function Emitter() {
this.dis1 = this;
};
function Person() {
Emitter.call(this); // calls the parent constructor.
this.dis2 = this;
};
Person.prototype = Object.create(Emitter.prototype); // you don't even use this line
var p1 = new Person();
var p2 = new Person();
In this model, p1.dist1 === p1
and p1.dist2 === p1
, p2.dist1 === p2
and p2.dist2 === p2
. What happens here is that the code in the Emitter constructor is called with this being the new Person object being created. So when you say in the Emitter constructor this.dis1 = this
, this
is actually p1
or p2
.
The line with the prototype is not used in this example, but it does other cool things you might want:
p1 instanceof Emitter
will be true.You can find this model also used on MDN.
Upvotes: 3
Reputation: 382514
The dis1
property is looked up in the prototype chain.
As it's not present in p1 or p2, it's looked up in the prototype, which is the object you created with new Emitter()
. That's the reason why you have the same value.
The prototypal model in JavaScript is very different from the inheritance model you have in other languages like Java. Be careful not to try to emulate Java or C++ designs in JavaScript : that always lead to bad ones.
Now, suppose you want to have an object at the Emitter level and different for each Person instance, then the simplest solution might be to initialize the values on demand or using a specific function (you might use a convention to name it initialize
and then override it).
Example with on demand initialization :
function Emitter() {
}
Emitter.prototype.register = function(thing){
if (!this.listeners) {
this.listeners = [];
}
this.listeners.push(thing);
}
function Person() {
}
Person.prototype = new Emitter();
In fact the best solution here would probably, as often in real OOP languages, to prefer composition over inheritance and to simply set the emitter as a property of the person.
Upvotes: 2
Reputation: 10536
As dystroy already mentioned, dis1
gets looked up in the prototype chain. If you want to have different dis1
s for different Person
s, you can use the following pattern.
function Emitter() {
this.dis1 = this;
};
function Person() {
Emitter.call(this);
this.dis2 = this;
};
Person.prototype = Object.create(Emitter.prototype);
var p1 = new Person();
var p2 = new Person();
console.log(p1.dis1 === p2.dis1); //false
console.log(p1.dis2 === p2.dis2); //false
Note that you have to call Emitter
inside the Person
constructor, while providing this
as current context. Creating a new Person will now create an object that contains all the (local) properties of Person
as well as all the (local) properties of Emitter
in a flat hierarchy:
{
dis1: Person,
dis2: Person
}
You also can use Object.create
to realize prototype inheritance. This has the advantage, that you don't actually call the Emitter
constructor and thereby you don't get "local" properties like dis1
to be inside the prototype object.
Upvotes: 1