What is wrong with this code? Can somebody help me with JavaScript Object Inheritance? I am starting to feel like an idiot!!
Thanks in advance, Sam
function Human(name, sex) {
this.name = name;
this.sex = sex;
};开发者_如何学Go
function Man(name) {
Man.prototype = new Human(name, "Male");
};
var m = new Man("Sam Striano");
alert(m.name); //<-- = Undefined
You want this instead:
function Man(name) {
Human.call(this, name, "Male");
}
What that code does
It seems like you're only trying to call the constructor of the parent, Human
, which isn't the same is prototypal inheritance. The code above takes the constructor for Human
and applies it to this
- a new Man
object.
What your code does
The line Man.prototype = new Human(name, "Male")
is changing the prototype of Man
every time a new Man is created. Not only that, you're completing re-assigning the prototype object, and so it will only apply to objects created after that assignment - i.e. not the first one. Hence, m.name
is undefined.
Proper prototypal inheritance
Note that calling the parent's constructor, as in my code above, won't cause Man
to automatically inherit any methods assigned to Human.prototype
. The best way to do this is to clone Human.prototype
into Man.prototype
but outside of any constructors. Like this:
function Man(name) {
Human.call(this, name, "Male");
}
function inherit(parent, child) {
if (Object.create) child.prototype = Object.create(parent.prototype);
else {
for (var key in parent.prototype) {
if (!parent.prototype.hasOwnProperty(key)) continue;
child.prototype[key] = parent.prototype[key];
}
}
}
inherit(Human, Man);
This may seem rather verbose, and the alternative may be to do this:
Man.prototype = new Human('no name', 'Male');
Which will work, but causes unwanted side-effects since we're forced to assign a dud name to the prototype
, and it's letting the constructor for Human
call an extra time just for assigning the prototype. Be warned if you go down this path and later change the Human
constructor to do more than just assign properties to this
.
There's usually two steps to mimic classical inheritance in javascript:
Your subclass constructor needs to call the parent constructor
Your subclass prototype needs to chain into the parent prototype
the first step usually looks like
function Subclass(blah) {
ParentClass.apply(this, arguments);
}
The second step is trickier. On JS environments that implement the __proto__
property, you could do
Subclass.prototype = {
__proto__ : ParentClass.prototype,
subclassMethod1: function() { /* ... */ }
}
Unless you know exactly where your script will run (like in a node.js environment), you can't rely on __proto__
being available to your script, so the general approach will require to use Crockford's object() method:
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
Subclass.prototype = Object.create(ParentClass.prototype);
Subclass.prototype.subclassMethod1 = function() { /* ... */ }
That's the gist of it. ES5 runtimes may have Object.create()
already built-in, and that's fine.
There are leftover things to complete the illusion of classic inheritance, such as the ability to easily call a parent class' overriden method. With what we have now, you'd need to have something like ParentClass.prototype.overridenMethod.call(this, arg1, arg2)
in your Subclass method.
Some OO libraries will helpfully define extra cruft on each of your subclass instances so you can use things like this.superclass.overridenMethod(arg1, arg2)
.
The implementation of that cruft is left as an exercise to the reader ;)
I think what you're after is to have the Man
class in inherit properties from Human
. You're on the right track, but would need to apply a new Human
instance once as the prototype object of Man
.
function Human(name, sex) {
this.name = "some default";
this.sex = sex;
};
function Man(name) {
if( name !== undefined )
this.name = name;
};
Man.prototype = new Human(name, "Male");
Man.prototype.constructor = Man;
var m = new Man("Sam Striano");
alert(m.name); // alerts "Sam Striano"
alert(m.sex); // alerts "Male"
As far as I know, you should handle all stuff with prototype
and constructor
and the inerithance could be managed in this way:
// Define superclass
function Human( name, sex ) {
this.name = name;
this.sex = sex;
}
// Define superclass methods
Human.prototype.method1 = function() {
alert( 'This is the call to ORIGINAL method1() with name: ' + this.name + ' and sex: ' + this.sex );
}
// Define subclass
function Man( name, age ) {
this.constructor.apply( this, [ name, 'Man' ] );
this.age = age;
}
// Define subclass inerithance
Man.prototype = new Human();
// Define subclass methods
Man.prototype.method1 = function() {
alert( 'This is the call to OVERWRITE method1() with name: ' + this.name + ' and sex: ' + this.sex + ' and age: ' + this.age );
this.constructor.prototype.method1.apply( this );
}
var m = new Man( 'Sam Satriano', 30 );
m.method1();
// Should alert:
// This is the call to OVERWRITE method1() with name: Sam Satriano and sex: Man and age: 30
// This is the call to ORIGINAL method1() with name: Sam Satriano and sex: Man
Hope this helps. Ciao!
Without getting into an inheritance fight, your problem can be solved by changing your code to the following:
function Human(name, sex) {
this.name = name;
this.sex = sex;
};
function Man(name) {
// This is how you call the parent's constructor
Human.call(this, name, "Male");
};
// The call to setup the prototype only needs to happen once
// Not in every instantiation of the object
Man.prototype = new Human();
// Have to fix the constructor, right now it's set to Human
Man.prototype.constructor = Man;
var m = new Man("Sam Striano");
>> m.name // outputs "Sam Striano";
>> m instanceof Human // outputs true
This is still not an ideal way to inherit. I posted something explaining what makes good JS inheritance. http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html
精彩评论