2011-01-24 2 views
2

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

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

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 

ответ

1

Там, как правило, два шага, чтобы имитировать классическое наследование в JavaScript:

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

  2. Вашего подкласс прототип должен цепь в родительском прототип

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

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

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

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

Если вы точно не знаете, где ваш скрипт будет работать (как в Node.js среды), вы не можете полагаться на __proto__ будучи доступной для вашего сценария , так что общий подход потребует использования 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() { /* ... */ } 

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

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

Реализация этого хлама остается в качестве упражнения для читателя;)

+0

Должны ли быть определены методы как в базе, так и в sub? Не могли бы вы показать мне полный пример базового классического наследования с использованием JavaScript [пожалуйста]? – Sam

2

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

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

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

+0

В чем разница между прототипом и «__prototype__»? – Sam

+0

Как String, Array, Number, Boolean и т. Д.? Итак, этот пример, который вы даете, не «прототипическое наследование»? – Sam

+0

@Sam, Моя ошибка, я думаю, вы имеете в виду '__proto__'. Это привязать новый объект к прототипу функции, которая его построила. Его не следует использовать, см. Это для получения дополнительной информации: https: //developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/proto –

1

Я думаю, что вы после этого, чтобы иметь 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" 
+0

Теперь я просто смущен между этим и ответом выше от «Box9» ??? – Sam

+0

@Sam: С какой частью вас смущают? – user113716

+0

@Sam: [Вот пример того, как я могу его реализовать] (http://jsfiddle.net/Xyg3s/). Есть пара элементов, прототипированных на «Человеческий», которые будут применяться ко всем «Людям». Тогда каждый экземпляр «Человек» может иметь свойство «sex», которое будет использоваться в роли родительского класса для «Человек» и «Женщина». Тогда каждый «Человек» может иметь собственное свойство «имя». Поскольку 'Human' имеет функцию' speak', которая предупреждает 'имя', каждый' Человек' может говорить свое собственное 'имя'. – user113716

1

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


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

Надеется, что это помогает. Чао!

+0

Btw, есть несколько интересных плагинов jQuery (например, low-pro https://github.com/danwrong/low-pro-for-jquery/), которые также имитируют «супер» поведение. – lomanf

+0

В конструкторе «Человек» вы хотите вызвать конструктор родителя, а не тот же конструктор. Ваш пример работает только потому, что вы не исправили свойство конструктора на прототипе после 'Man.prototype = new Human()', как вы должны при настройке наследования –

1

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

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-inheritance-done-right.html

Смежные вопросы