2012-06-18 8 views
7

Nie rozumiem tego zachowania w JavaScript do dziedziczenia Zawsze widać to zdefiniowane tak:Dlaczego nie miałbym używać Child.prototype = Parent.Prototype zamiast Child.prototype = new Parent(); dla dziedziczenia JavaScript?

function GameObject(oImg, x, y) { 

    this.x = x; 
    this.y = y; 
    this.img = oImg; 

    this.hit = new Object(); 
    this.hitBox.x = x; 
    this.hitBox.y = y; 
    this.hitBox.width = oImg.width; 
    this.hitBox.height = oImg.height; 

} 

Spaceship.prototype = new GameObject(); 
Spaceship.prototype.constructor = Spaceship; 

function Spaceship(){ 
    console.log("instantiate ship"); 
    GameObject.apply(this, arguments); 
    this.vx = 0; 
    this.vy = 0; 
    this.speed = 3; 
    this.friction = 0.94; 
} 

Ale w moim przypadku te linie:

this.hitBox.width = oImg.width; 
    this.hitBox.height = oImg.height; 

Kiedy zrobić konsolę .log (this) w moim konstruktorze Spaceship, widzę, że właściwość jest ustawiona na Spaceship zamiast GameObject, jeśli je usunę, zostanie ustawiona na GameObject.

A jeśli używam:

Spaceship.prototype = GameObject.prototype; 

mam żadnych problemów z tym. Powodem tego, że bloki mnie to, że mam inny obiekt z metody add() i sprawdza, że ​​inerhits Przedmiot GameObject z tym kodem:

if(object instanceof GameObject) 

Nie rozumiem, co te dwie linie mogą prawdopodobnie zmiana tak, że dziedzictwo jest zerwane, gdy są obecne i nie jestem pewien, czy po dziedziczeniu druga droga jest dobra. Czy ktoś mógłby mnie o tym poinformować? :)

+0

Zwróć uwagę, że 'instanceof' jest złą praktyką, jeśli możesz tego uniknąć, prawdopodobnie powinieneś. – Jasper

+1

@Jasper: Dlaczego? – Bergi

+0

@Bergi Nie jest właściwe użycie polimorfizmu i może powodować problemy, gdy więcej klas zostanie dodanych do twojego drzewa dziedziczenia. Innymi słowy, 'instanceof' zazwyczaj nie miesza się bardzo dobrze z zasadą Open/Closed. – Jasper

Odpowiedz

13

Jeśli nie

Spaceship.prototype = GameObject.prototype;

Potem oba odnoszą się do tego samego obiektu, więc równie dobrze można mieć wszystkiego w GameObject, jeśli coś dodać do Spaceship.prototype, zostanie on dodany do GameObject.prototype także. Możesz go łatwo przetestować, dodając coś po Spaceship.prototype po przypisaniu. For example, in your case you can see that GameObject.prototype.constructor is actually Spaceship.

chodzi o

Spaceship.prototype = new GameObject(); 

To wywołuje konstruktor, który może mieć niepożądane skutki uboczne, czy raczej chcesz używać:

Spaceship.prototype = Object.create(GameObject.prototype); 

Jeżeli używany Object.create funkcjonalność tutaj sprowadza się do:

Object.create = function(proto) { 
    function f(){} 
    f.prototype = proto; 
    return new f; 
}; 

Nowoczesne przeglądarki mają już tę funkcję.

+0

Dziękuję bardzo, że rozwiązałam mój problem i teraz lepiej rozumiem dziedziczenie! :) –

+0

@GeoffreyHug zauważyłeś, że nie definiujesz '.hitBox' w dowolnym miejscu? 'this.hit = new Object(); this.hitBox.x = x; 'powinien prawdopodobnie być' this.hitBox = new Object(); ' – Esailija

+0

Tak, widziałem to zaraz po zamieszczeniu komentarza, dokonałem tylu zmian ^^ –

2

Nigdy nie wyjaśniono, dlaczego dziwnie się zachowujesz, używając this.hitBox (myślę, że właśnie to próbujesz powiedzieć).

Jeśli do dziedziczenia przez wywołanie konstruktora rodzica do stworzenia prototypu, konstruktor rodzic jest wykonywany raz aby utworzyć wystąpienie typu nadrzędnego, a następnie wszystkie instancje typu dzieci będą dzielić się jedną instancję jako ich prototyp.

Problem polega na tym, że jeśli konstruktor ma jakieś wiersze, które przypisać zmienne obiektów do this, następnie te obiekty będą właściwości na tym prototypie i wszelkie modyfikacje tych obiektów zostaną odzwierciedlone w poprzek wszystkie instancje typu dziecięcej :

Spaceship.prototype = new GameObject(); 
Spaceship.prototype.constructor = Spaceship; 

var sps1 = new Spaceship(); 
var sps2 = new Spaceship(); 

sps1.hitBox.x = 9; 
sps2.hitBox.x = 12; 
console.log(sps1.hitBox.x); // 12 (oh noes! what happened) 
console.log(sps2.hitBox.x); // 12 

(istnieją inne, podobne problemy z „call konstruktor zrobić prototyp” podejście, ale ja po prostu zostawić go tutaj na tym punkcie)

@ sugestia Esailija do korzystania Object.create(baseObject) jest t on pierwszy krok do rozwiązania tego problemu. Tworzy nowy obiekt, którego prototyp to baseObject, ale bez elementów skonfigurowanych w konstruktorze (jest to dobra rzecz, ale musi być uwzględniona. Czytaj dalej ...).

Jak już powiedziałem, stworzy to obiekt, w którym logika inicjalizacji w konstruktorze rodzica nigdy się nie uruchomiła, ale w większości przypadków ta logika jest istotna dla funkcji obiektu. Więc jest jeszcze jedna rzecz, którą trzeba zrobić, co ma mieć konstruktor dziecko wywołać konstruktor nadrzędny:

function Spaceship(oImg, x, y) { 
    // call parent constructor on this object and pass in arguments. 
    // you could also use default values for the arguments when applicable 
    GameObject.call(this, oImg, x, y); 

    // remainder of Spaceship constructor... 
} 

To zapewni, że logika konstruktor rodzic pracuje oddzielnie dla każdego nowego Spaceship i przeprowadza niezbędne zadania inicjowania.

0
function GameObject(oImg, x, y) { 

    this.x = x; 
    this.y = y; 
    this.img = oImg || {width:null, height: null}; 

    this.hitBox = new Object(); 
    this.hitBox.x = x; 
    this.hitBox.y = y; 
    this.hitBox.width = this.img.width; 
    this.hitBox.height = this.img.height; 

} 


function Spaceship(){ 
    GameObject.apply(this, arguments); 
    this.vx = 0; 
    this.vy = 0; 
    this.speed = 3; 
    this.friction = 0.94; 
} 
Spaceship.prototype = new GameObject(); 

var sps1 = new Spaceship(); 
var sps2 = new Spaceship(); 

sps1.hitBox.x = 9; 
sps2.hitBox.x = 12; 
console.log(sps1.hitBox.x); // 9 
console.log(sps2.hitBox.x); // 12 
Powiązane problemy