Наследование объектов JavaScript - что я делаю не так? - PullRequest
2 голосов
/ 25 января 2011

Что не так с этим кодом? Может кто-нибудь помочь мне с наследованием объектов JavaScript? Я начинаю чувствовать себя идиотом !!

Спасибо заранее, Sam

function Human(name, sex) {
    this.name = name;
    this.sex = sex;
};

function Man(name) {
    Man.prototype = new Human(name, "Male");
};

var m = new Man("Sam Striano");
alert(m.name); //<-- = Undefined

Ответы [ 5 ]

2 голосов
/ 25 января 2011

Вы хотите это вместо:

function Man(name) {
    Human.call(this, name, "Male");
}

Что этот код делает

Похоже, вы пытаетесь вызвать только конструктор родителя, Human, что не является наследованием прототипа. Приведенный выше код принимает конструктор для Human и применяет его к this - новому объекту Man.

Что делает ваш код

Линия Man.prototype = new Human(name, "Male") меняет прототип Man каждый раз, когда создается новый Человек . Мало того, вы завершаете переназначение объекта-прототипа, и поэтому он будет применяться только к объектам, созданным после этого назначения - то есть не к первому. Следовательно, m.name не определено.

Правильное наследование прототипа

Обратите внимание, что вызов конструктора родителя, как в моем коде выше, не заставит Man автоматически наследовать любые методы, назначенные Human.prototype. Лучший способ сделать это - клонировать Human.prototype в Man.prototype, но вне каких-либо конструкторов. Как это:

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);

Это может показаться довольно многословным, и альтернатива может заключаться в следующем:

Man.prototype = new Human('no name', 'Male');

Который будет работать , но вызывает нежелательные побочные эффекты, так как мы вынуждены присвоить тупое имя для prototype, и это позволяет конструктору для Human вызывать дополнительное время только для Назначение прототипа. Будьте осторожны, если вы пойдете по этому пути и позже измените конструктор Human, чтобы он делал больше, чем просто присваивал свойства this.

1 голос
/ 25 января 2011

Не вступая в борьбу с наследованием, вашу проблему можно решить, изменив код на следующий:

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

Это все еще не идеальный способ наследования. Я опубликовал кое-что, объясняющее, что делает хорошее наследование JS. http://js -bits.blogspot.com / 2010/08 / JavaScript наследование сделано-right.html

1 голос
/ 25 января 2011

Насколько я знаю, вы должны обрабатывать все вещи с помощью prototype и constructor, а с инерционностью можно управлять следующим образом:


// 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

Надеюсь, это поможет. Ciao!

1 голос
/ 25 января 2011

Я думаю, вам нужно иметь класс Man в свойствах наследования от Human. Вы на правильном пути, но вам нужно будет применить новый экземпляр Human один раз в качестве объекта-прототипа 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"
1 голос
/ 25 января 2011

В javascript обычно есть два шага для имитации классического наследования:

  1. Ваш конструктор подкласса должен вызвать родительский конструктор

  2. Ваш прототип подкласса должен быть связан с родительским прототипом

первый шаг обычно выглядит как

function Subclass(blah) {
    ParentClass.apply(this, arguments);
}

Второй шаг сложнее. В средах JS, которые реализуют свойство __proto__, вы можете сделать

Subclass.prototype = {
    __proto__ : ParentClass.prototype,
    subclassMethod1: function() { /* ... */ }
}

Если вы точно не знаете, где будет запускаться ваш скрипт (как в среде node.js), вы не можете полагаться на то, что __proto__ будет доступен для вашего скрипта, поэтому общий подход потребует использования объекта Крокфорда () метод :

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() { /* ... */ }

В этом суть. Время выполнения ES5 может быть уже Object.create() встроенным, и это нормально.

Есть еще кое-что, чтобы завершить иллюзию классического наследования, например, возможность легко вызывать переопределенный метод родительского класса. С тем, что мы имеем сейчас, вам нужно иметь что-то вроде ParentClass.prototype.overridenMethod.call(this, arg1, arg2) в вашем методе подкласса. Некоторые библиотеки OO будут полезны для определения лишних ошибок в каждом из ваших экземпляров подкласса, чтобы вы могли использовать такие вещи, как this.superclass.overridenMethod(arg1, arg2).

Реализация этой фразы оставлена ​​читателю в качестве упражнения;)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...