2012-06-18 9 views
7

No entiendo este comportamiento en javascript para la herencia siempre he visto que define de este modo:¿Por qué no debería usar Child.prototype = Parent.Prototype en lugar de Child.prototype = new Parent(); para la herencia de 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; 
} 

Pero en mi caso, estas líneas:

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

Cuando hago una consola .log (esto) en mi constructor de Nave Espacial, puedo ver que la propiedad proto está configurada como Nave Espacial en lugar de GameObject, si las elimino, se configura como GameObject.

Y si uso:

Spaceship.prototype = GameObject.prototype; 

no tengo más problemas con eso. La razón de que esto bloquea mí es que tengo otro objeto con un método add() y comprueba que el objeto de inerhits GameObject con este código:

if(object instanceof GameObject) 

que no entienden lo que esas dos líneas, probablemente, puede cambiar para que la herencia se rompa cuando están presentes y no estoy seguro de hacer herencia, la segunda forma es buena. ¿Podría alguien aclararme sobre esto por favor? :)

+0

Tenga en cuenta que 'instanceof' es una mala práctica, si puede evitar usarlo, probablemente debería. – Jasper

+1

@Jasper: ¿Por qué? – Bergi

+0

@Bergi No es un uso apropiado del polimorfismo y puede causar problemas cuando se agregan más clases a su árbol de herencia. En otras palabras, 'instanceof' generalmente no se combina muy bien con el principio Abierto/Cerrado. – Jasper

Respuesta

13

Si lo hace

Spaceship.prototype = GameObject.prototype;

Luego ambos se refieren al mismo objeto, por lo que también podría tener todo en GameObject, si se agrega algo a Spaceship.prototype, que se añadirá a GameObject.prototype también. Puede probarlo fácilmente agregando algo al Spaceship.prototype después de la asignación. For example, in your case you can see that GameObject.prototype.constructor is actually Spaceship.

En cuanto a

Spaceship.prototype = new GameObject(); 

Esto invoca el constructor que podría tener efectos secundarios no deseados, que más bien desea utilizar:

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

Cuando el utilizado Object.create funcionalidad que aquí se reduce a:

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

Los navegadores modernos ya tienen esta función.

+0

Muchas gracias, esto solucionó mi problema y ahora entiendo mejor la herencia. :) –

+0

@GeoffreyHug ¿Notaste que no defines '.hitBox' en ninguna parte? 'this.hit = new Object(); this.hitBox.x = x; 'probablemente debería ser' this.hitBox = new Object(); ' – Esailija

+0

Sí, lo vi justo después de publicar el comentario, hice tantos cambios ^^ –

2

Nunca se explicaba adecuadamente por qué tenías un comportamiento extraño con this.hitBox (creo que eso es lo que intentabas decir).

Si lo hace la herencia invocando el constructor de la matriz para crear un prototipo, el constructor de ese padre se ejecuta vez para crear una instancia del tipo de los padres, y luego todos instancias del tipo de niño compartirá de que una instancia como su prototipo.

El problema con esto es que si ese constructor tiene ninguna línea que asignan los objetos mutables a this, entonces esos objetos serán propiedades en ese prototipo y cualquier modificación a esos objetos se reflejarán en toda todos instancias del tipo de niño :

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 

(hay otros problemas similares con el "llamar a un constructor para hacer un prototipo" enfoque, pero sólo voy a dejarlo aquí en ese punto)

@ sugerencia de Esailija utilizar Object.create(baseObject) es t El primer paso para resolver este problema. Crea un nuevo objeto cuyo prototipo es baseObject, pero sin las cosas que se configuran en el constructor (Esto es algo bueno, pero debe tenerse en cuenta. Seguir leyendo ...).

Como acabo de decir, esto creará un objeto donde la lógica de inicialización en el constructor padre nunca se ha ejecutado, pero en la mayoría de los casos esa lógica es relevante para la funcionalidad del objeto. Así que hay una cosa más que hay que hacer, lo que es tener el constructor niño llama al constructor padre:

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

Esto asegurará que la lógica del constructor padre corre separado por cada nuevo Spaceship, y lleva a cabo las tareas de inicialización necesarias.

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 
Cuestiones relacionadas