JavaScript prototyping: single prototype object or not?
I don't really get JavaScript prototyping. Take this code, for instance:
function Class(asdf) {
if(typeof(asdf) == 'undefined') {
} else {
this.asdf = asdf;
}
}
Class.prototype.asdf = "default_asdf";
Class.prototype.asdf2 = [];
Class.prototype.change_asdf = function() {
this.asdf = "changed_asdf";
this.asdf2.push("changed_asdf2");
}
function SubClass() {
}
SubClass.prototype = new Class("proto_class");
SubClass.prototype.constructor = SubClass;
test1 = new SubClass();
alert("test1 asdf: " + test1.asdf + " " + test1.asdf2);
test1.change_asdf();
alert("test1 asdf: " + test1.asdf + " " + test1.asdf2);
test2 = new SubClass();
alert("test2 asdf: " + test2.asdf + " " + test2.asdf2);
The first alert prints "proto_class []", as expected. The second alert prints "changed_asdf [changed_asdf2]", also as expected. But why does the third alert print "proto_class [changed_asdf2]"?! If the original prototype object (new Class("proto_clas开发者_高级运维s")) is being modified, then why doesn't the asdf variable remain "changed_asdf"? And if it isn't, then why does the asdf2 array contain "changed_asdf2"? Furthermore, how do I make sure that each new SubClass() instance contains a fresh instance of Class(), as in C++ and Java?
It's because you write
SubClass.prototype = new Class("proto_class");
you are creating a prototype of one instance of Class
. What you want is to create a sub-class that inherits from its parent's prototype. As David Flanagan shows in his JavaScript: The Definitive Guide (§ 9.5), you have to use a helper function to create a new object with a specified prototype:
function heir(p) {
function f(){} // dummy constructor function
f.prototype = p; // specify prototype object we want
return new f(); // create and return new object
}
(Crockford calls this function Object.create
, after the so-called ES5 object constructor property, but please don't do this, as this can be misleading.)
Within the SubClass constructor, you have to call the Class constructor with this
set to the current object:
function SubClass() {
// call the parent's constructor with
// `this` set to the current scope
Class.call(this, "proto_class");
}
And last, but not least, you only reset Class.asdf2
once, but not in the constructor function of Class
or SubClass
. So add this.asdf2 = [];
to one of the constructors.
The complete code now reads:
function heir(p) {
function f(){} // dummy constructor function
f.prototype = p; // specify prototype object we want
return new f(); // create and return new object
}
function Class(asdf) {
if (typeof asdf != 'undefined')
this.asdf = asdf;
}
Class.prototype.asdf = "default_asdf";
Class.prototype.asdf2 = [];
Class.prototype.change_asdf = function() {
this.asdf = "changed_asdf";
this.asdf2.push("changed_asdf2");
}
function SubClass() {
// call the parent's constructor with
// `this` set to the current scope
Class.call(this, "proto_class");
this.asdf2 = [];
}
SubClass.prototype = heir(Class.prototype);
SubClass.prototype.constructor = SubClass;
test1 = new SubClass();
alert("test1 asdf: " + test1.asdf + " " + test1.asdf2);
test1.change_asdf();
alert("test1 asdf: " + test1.asdf + " " + test1.asdf2);
test2 = new SubClass();
alert("test2 asdf: " + test2.asdf + " " + test2.asdf2);
This is because asdf2
is a mutable array on Class.prototype
. That array is shared by all instances that delegate to that prototype. If you want each instance to have a separate asdf2
, you have to assign it to this.asdf2
in some method.
Note that you assign this.asdf
, but you never assign this.asdf2
, you just push on to the existing array.
var house = {nice: true};
var me = {home: house};
var roomie = {home: house};
// Now roomie has a party and trashes the place.
roomie.home.nice = false;
//and how's my house?
me.home.nice === false;
// because house is shared.
The sharing of house
in this example is the same as the sharing of asdf2
in the question.
精彩评论