Reputation: 6749
Here is an example:
var Box = function() {
this.id = generateUniqueId();
};
Box.prototype = {
add:function(parent) {
parent.appendChild(this.elm);
}
};
var NewBox = function() {
this.newThing = true;
};
NewBox.prototype = new Box();
NewBox.prototype.remove = function() {
this.elm.parentNode.removeChild(this.elm);
};
var a = new NewBox();
var b = new NewBox();
alert(a.id); // alerts 0
alert(b.id); // also alerts 0! :@
I would like to have a and b (basically each time I create a NewBox
) have their own id
. I understand that NewBox
is using prototypal inheritance and is thus inheriting from a single instance of the generated id
that it gets from Box
, but I would like it so that each NewBox
gets its own id
without having to explicitly say that inside the NewBox
constructor.
Maybe it's impossible or I'm doing something really wrong, so please help!
Thanks a lot!
Upvotes: 0
Views: 176
Reputation: 827236
In your example, the Box
constructor gets executed only when you set the NewBox.prototype
object.
You could workaround this by calling the Box
constructor function inside NewBox
with the Function.prototype.apply
method, to set the this
value and forward all the argument values, for example:
//..
var NewBox = function() {
Box.apply(this, arguments);
this.newThing = true;
};
//..
var a = new NewBox();
var b = new NewBox();
// assuming your `generateUniqueId` function
// increments a number
alert(a.id); // will alert 1
alert(b.id); // will alert 2
Now, each time the NewBox
constructor is called to create a new object instance (new NewBox();
), it will call the Box
function to apply all its logic on it. This will help you to avoid repeating the logic of the parent constructor over and over.
The apply
, is used call the "parent" constructor function setting the this
value to the object instance that is being created by the NewBox
constructor and we pass all arguments provided to this function.
Your example also shows a common problem, when you express inheritance relationship through NewBox.prototype = new Box();
the Box
constructor gets called and it has side effects, this new object will be initialized and your generateUniqueId
function will be executed for the first time, if you want to avoid that, you need to either use a temp constructor, just to make a new object that inherits from Box.prototype
, or use the new ECMAScript 5 Object.create
method for the same purpose, for example:
function inherit(o) {
function Tmp() {}
Tmp.prototype = o;
return new Tmp();
}
//.....
NewBox.prototype = inherit(Box.prototype);
Or:
NewBox.prototype = Object.create(Box.prototype);
In that way, you express the same inheritance hierarchy without running your Box
constructor that first time, avoiding any side effect that it might cause.
At last but not least, whenever you replace a function's prototype property is always recommended to restore the constructor
property of this new prototype object, otherwise it will point to the wrong function.
In your example, since you replace the NewBox.prototype
with a new instance of Box
, the NewBox.prototype.constructor
property will point to Box
, (your instances are affected, e.g. a.constructor === Box; // true
) instead of to NewBox
as you would expect, we need to set it back, e.g.:
NewBox.prototype = someObject; // as any of the examples above
NewBox.prototype.constructor = NewBox;
You could abstract those details into a function, as I did in the inherit
function above:
function inherits(child, parent) {
var obj, Tmp = function () {};
Tmp.prototype = parent.prototype;
obj = new Tmp();
child.prototype = obj;
child.prototype.constructor = child;
}
//...
// instead of setting the `NewBox.prototype` manually
inherits(NewBox, Box); // "NewBox inherits from Box"
//...
Upvotes: 6
Reputation: 22319
Matt Ball has the right idea. Instead try:
var Box = (function(){
var numberOfBoxes = 0;
function() {
this.id = numberOfBoxes++;
}
})();
Or in the case you want all your (different) classes to have unique ids:
var generateUniqueID = (function(){
var runningCount = 0;
return function (){
return runningCount++;
}
})();
var Box = function() {
this.id = generateUniqueId();
};
var NewBox = function() {
this.id = generateUniqueId();
this.newThing = true;
};
Upvotes: 1
Reputation: 359776
Maybe it's impossible or I'm doing something really wrong, so please help!
You're doing something wrong.
If you want instance-specific values, initialize them in the constructor, not the prototype.
Upvotes: 2