2009-10-07 14 views
16

Esto parece inconsistente, pero probablemente se deba al hecho de que soy nuevo en la función de herencia del prototipo de JavaScript.javascript prototype inheritance

Básicamente, tengo dos propiedades de clase base, "lista" y "nombre". Instalo dos subclases y le doy valores a las propiedades. Cuando instancia la segunda subclase, obtiene los valores de la lista de la primera instancia de subclase, pero solo para la "lista" y no para "nombre". ¿¿Que esta pasando?? Por supuesto, preferiría que cualquier instancia de subclase subsiguiente no obtenga valores de otras instancias, pero si eso va a suceder, ¡debería ser consistente!

Aquí es un fragmento de código:

function A() 
    { 
     this.list = []; 
     this.name = "A"; 
    } 
    function B() 
    { 
    } 
    B.prototype = new A(); 

    var obj1 = new B(); 
    obj1.list.push("From obj1"); 
    obj1.name = "obj1"; 

    var obj2 = new B(); 

    //output: 
    //Obj2 list has 1 items and name is A 
    //so, the name property of A did not get replicated, but the list did 
    alert("Obj2 list has " + obj2.list.length + " items and name is "+ obj2.name);  

Respuesta

4
  • ¿Estás diciendo que en este código B es un implementación de A.
  • Entonces estás diciendo obj1 es una nueva instancia de B y así debería agarrar todos de los valores de A.
  • A continuación, se añade un elemento a la ubicación de referencia de la lista en obj2 que a su vez añade un elemento a la ubicación de referencia de la lista en un (ya que hacen referencia a la misma cosa).
  • Luego cambia el valor del nombre en obj1.
  • Entonces dices que obj2 es una NUEVA instancia de B de , por lo que debes tomar todos los de los valores de A.

Ha cambiado los valores de la lista a la que se hace referencia en A pero no la referencia en sí. obj2.name debe ser "A".

3

Cuando inserta un objeto nuevo en obj1.list está mutando la lista existente en el prototipo. Cuando cambia el nombre, está asignando una propiedad en obj1, la instancia, , no el prototipo. Tenga en cuenta que ocurriría lo mismo si lo hubiera hecho:

obj1.list = ["from obj1"] 
... 
console.log(obj2.list) // <--- Will log [] to console 
2

Está reasignando la variable de nombre. Lo que está sucediendo es que estás reasignando una nueva variable de nombre a obj1. Su declaración de nombre está anulando la declaración en el objeto prototipo (para que no lo vea). Con la lista, estás mutando la lista en lugar de cambiarla.

13

Lo que sucede con los prototipos es que puedes leer todo el día y no cambia la estructura subyacente de lo que apunta a qué. Sin embargo, la primera vez que realiza una tarea, está reemplazando, en esa instancia, a lo que apunta esa propiedad.

En su caso, en realidad no reasignó la propiedad del prototipo, modificó el valor de la estructura subyacente que se encontró en esa propiedad.

Lo que esto significa es que todos los objetos que comparten un prototipo de una realidad cuota de la implementación de A. Eso significa cualquier estado realizado en A se encuentran en todas las instancias de B. En el momento en que haces una misión a ese propiedad en una instancia de B, ha reemplazado efectivamente a lo que señala esa instancia (y solo esa instancia) (creo que esto se debe al hecho de que hay un nuevo "nombre" de propiedad en B que se golpea en la cadena de alcance, antes alcanza la implementación de "nombre" en A).

EDIT:

Para dar un ejemplo más explícito de lo que está pasando en:

B.prototype = new A(); 
var obj1 = new B(); 

Ahora, la primera vez que hacemos una lectura, el intepreter hace algo como esto:

obj1.name; 

Intérprete: "Necesito el nombre de la propiedad. Primero, marque B. B no tiene 'nombre', así que continuemos por la cadena de alcance. Luego, A. Aha! A tiene 'nombre', devuelva ese"

Sin embargo, cuando se escribe, el intérprete en realidad no se preocupa por la propiedad heredada.

obj1.name = "Fred"; 

intérprete: "Necesito asignar a la propiedad 'nombre' que estoy dentro del alcance de esta instancia de B, por lo asigno 'Fred' a B. Pero dejo todo lo demás más abajo en la. cadena de ámbito sola (es decir, a) "

Ahora, la próxima vez que hagas una lectura ...

obj1.name; 

intérprete:".! necesito propiedad 'nombre' Aha este ejemplo de B tiene la propiedad 'nombre' ya está en ella. Solo devuelva eso - No me importa el resto de la cadena de ámbito (es decir, A.name) "

Por lo tanto, la primera vez que escriba un nombre, lo insertará como una propiedad de primera clase en la instancia, y ya no le importa lo que está en AAname es todavía hay, está más abajo en la cadena de alcance, y el intérprete JS no llega tan lejos antes de encontrar lo que estaba buscando.

If " nombrar" tenía un valor predeterminado en un (a medida que tenemos, que es 'a'), entonces se podría ver este comportamiento:

B.prototype = new A(); 
var obj1 = new B(); 
var obj2 = new B(); 

obj1.name = "Fred"; 

alert(obj1.name); // Alerts Fred 
alert(obj2.name); // Alerts "A", your original value of A.name 

Ahora, en el cas e de su matriz, en realidad nunca reemplazó la lista en la cadena de alcance con una nueva matriz. Lo que hizo fue tomar un control en la matriz en sí, y le agregó un elemento. Por lo tanto, todas las instancias de B se ven afectadas.

Intérprete: "Necesito obtener la 'lista' como corresponde y agregarle un elemento. Al revisar esta instancia de B ... no, no tiene una propiedad de 'lista'. Siguiente en la cadena de alcance: A Sí., A tiene 'lista', el uso que Ahora, empuje en esa lista"

esto no ser el caso si usted hizo esto:.

obj1.list = []; 
obj1.push(123); 
+0

Gracias a todos por las explicaciones. Entonces, a pesar de que dice: B.prototype = new A(); y obj2 = new B(); La propiedad "list" de A() no se está creando de nuevo, solo se recicla. De todos modos, puedo aceptar este destino :) Entonces, ¿cuál es la mejor manera de tener una clase base que contiene una matriz? Estoy construyendo un árbol y necesito una clase genérica de nodo con niños. – med

+1

Muy buena explicación. –

0

este comportamiento se debe a que eres no asignando un valor a "list", lo estás modificando.

Este es un comportamiento perfectamente sensato cuando se da cuenta de cómo funciona la cadena de prototipos. Cuando hace referencia a obj1.list, primero se ve si existe "list" en obj1, lo usa si se encuentra y utiliza el que se encuentra en obj1.prototype (que es MyClass.prototype.list).

Así:

obj1.list.push("test"); // modifies MyClass.prototype.list 
obj1.list = ["new"]; // creates a "list" property on obj1 
obj1.list.push("test"); // modifies obj1.list, not MyClass.prototype.list 
delete obj1.list; // removes the "list" property from obj1 
// after the delete, obj1.list will point to the prototype again 
obj1.list.push("test"); // again modifies MyClass.prototype.list 

El más importante de comida para llevar es la siguiente: "prototipos no son clases". Los prototipos pueden hacer un trabajo razonablemente bueno de falsificación de clases, pero no son clases, por lo que no debe tratarlos como tales.

4

Por supuesto, yo preferiría que cualquier instancias de subclases posteriores no consiguen valores de otras instancias, pero si que va a pasar, hay que coherente!

Entonces no herede de una nueva instancia de A, herede del prototipo A. Esto asegurará que no heredes el estado, solo heredas el comportamiento. Esa es la parte difícil de la herencia prototípica, también hereda el estado, no solo el comportamiento como en la herencia OOP clásica. En JavaScript, las funciones del constructor solo deberían usarse para configurar el estado de la instancia.

var A = function (list) { 
    this.list = list; 
    this.name = "A"; 
}; 

A.prototype.getName = function() { 
    return this.name; 
}; 

var B = function (list) { 
    this.list = list; 
    this.name = "B"; 
}; 

B.prototype = A.prototype; // Inherit from A's prototype 
B.prototype.constructor = B; // Restore constructor object 

var b = new B([1, 2, 3]); 

// getName() is inherited from A's prototype 
print(b.getName()); // B 

Y, por cierto, si quieres cambiar algo en A, a través de B, que tiene que hacer esto:

B.prototype.name = "obj1"; 
+0

Excelente información, gracias. –