开发者

JavaScript Object Inheritance - What am I doing wrong?

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:

  1. Your subclass constructor needs to call the parent constructor

  2. 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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜