En el artículo anterior, se introdujo el concepto de prototipo y la relación entre los tres buenos amigos del constructor, el objeto prototipo e instancia en JavaScript: cada constructor tiene un "guardián": el objeto prototipo, y también hay una "posición" del constructor en el corazón del objeto prototipo. Los dos están enamorados, pero la instancia está "secretamente enamorada" del objeto prototipo, y ella también mantiene la posición del objeto prototipo en su corazón.
JavaScript en sí no es un lenguaje orientado a objetos, sino un lenguaje basado en objetos. Para aquellos que están acostumbrados a otros idiomas OO, al principio es un poco incómodo porque no hay concepto de "clase" aquí, o no hay distinción entre "clase" e "instancia", y mucho menos la diferencia entre "clase principal" y "subclase". Entonces, ¿cómo se vinculan estos montones de objetos en JavaScript de esta manera?
Afortunadamente, JavaScript proporcionó un método de implementación de "herencia" al comienzo de su diseño. Antes de comprender la "herencia", ahora entenderemos el concepto de cadenas prototipo.
Cadena prototipo
Sabemos que los prototipos tienen un puntero al constructor. ¿Qué pasa si hacemos que el objeto prototipo de subclase sea igual a otra instancia de la nueva SuperClass ()? En este momento, el objeto prototipo de subclase contiene un puntero al prototipo de superclase, y el prototipo de superclase también contiene un puntero al constructor de superclase. . . De esta manera, se forma una cadena prototipo.
El código específico es el siguiente:
function superclass () {this.name = "Women"} superclass.prototype.saywhat = function () {return this.name + ": i`ma girl!"; } function subclass () {this.subname = "tu hermana"; } Subclass.prototype = new SuperClass (); Subclass.prototype.subsaywhat = function () {return this.subname + ": i`ma hermosa niña"; } var sub = new subclass (); console.log (sub.saywhat ()); // Mujeres: ¡I`ma Girl!Use la cadena prototipo para lograr la herencia
En el código anterior, podemos ver que la subclase hereda las propiedades y métodos de superclase. Esta implementación hereditaria es asignando la instancia de superclase al objeto prototipo de subclase. De esta manera, el objeto prototipo de subclase se sobrescribe por una instancia de superclase, que tiene todas sus propiedades y métodos, y también tiene un puntero al objeto prototipo de superclase.
Hay algunas cosas a las que deben prestarse atención al implementar la herencia utilizando cadenas prototipo:
Presta atención a los cambios en el constructor después de la herencia. Aquí el constructor de subcontratación a superclase porque el prototipo de subclase apunta a superclase. Al comprender la cadena prototipo, no ignore el objeto predeterminado del objeto al final, por lo que podemos usar métodos incorporados como tostring en todos los objetos.
Al implementar la herencia a través de la cadena prototipo, no puede usar la definición literal del método prototipo, porque esto reescribirá el objeto prototipo (también introducido en el artículo anterior):
function superclass () {this.name = "Women"} superclass.prototype.saywhat = function () {return this.name + ": i`ma girl!"; } function subclass () {this.subname = "tu hermana"; } Subclass.prototype = new SuperClass (); Subclass.prototype = {// El objeto prototipo se sobrescribe aquí porque los atributos y los métodos de superclase no se pueden heredar subsayos: function () {return this.subname + ": i`ma beautiful girl"; }} var sub = new subclass (); console.log (sub.saywhat ()); // typeError: Undefined no es una funciónUn problema con el intercambio de instancias. Al explicar el prototipo y el constructor anteriormente, una vez presentamos que los prototipos que contienen atributos de tipo de referencia serán compartidos por todas las instancias. Del mismo modo, las propiedades del tipo de referencia en el prototipo de "clase principal" también se compartirán en el prototipo. Cuando modificamos los atributos de tipo de referencia de la "clase principal" a través de la herencia prototipo, todas las demás instancias heredadas del prototipo se verán afectadas. Esto no solo desperdicia recursos, sino también un fenómeno que no queremos ver:
function superclass () {this.name = "Women"; this.bra = ["A", "B"]; } function subclass () {this.subname = "tu hermana"; } Subclass.prototype = new SuperClass (); var sub1 = new Subclase (); sub1.name = "hombre"; sub1.bra.push ("c"); console.log (sub1.name); // man console.log (sub1.bra); // ["a", "b", "c"] var sub2 = new subclass (); console.log (sub1.name); // woman console.log (sub2.bra); // ["a", "b", "c"]Nota: Agregue un elemento a la matriz aquí, todas las instancias heredadas de la superclase se verán afectadas, pero si modifica el atributo de nombre, no afectará otras instancias, porque la matriz es un tipo de referencia y el nombre es un tipo primitivo.
¿Cómo resolver el problema del intercambio de instancias? Sigamos mirando hacia abajo ...
Herencia clásica (robo de constructor)
Como hemos presentado que rara vez usamos prototipos para definir objetos solos, en el desarrollo real, rara vez usamos cadenas prototipo solas. Para resolver el problema de compartir los tipos de referencia, los desarrolladores de JavaScript han introducido el patrón de herencia clásico (algunas personas llaman herencia de constructor prestado). Su implementación es muy simple de llamar a constructores Supertype en constructores de subtipo. Necesitamos usar la función de llamada () o aplicar () proporcionada por JavaScript, echemos un vistazo al ejemplo:
function superclass () {this.name = "Women"; this.bra = ["A", "b"];} function subclass () {this.subname = "tu hermana"; // Asigne el alcance de la superclase al constructor actual para implementar la herencia de superclass.call (this);} var sub1 = new subclass (); sub1.bra.push ("c"); console.log (sub1.bra); // ["A", "b", "c"] var sub2 = nuevo Subclass (); console.log (sub2.bra); // ["A", "B"]Superclass.call (esto); Esta oración significa que el trabajo de inicialización del constructor de superclase se llama en el entorno de subclase de instancia (contexto), de modo que cada instancia tendrá su propia copia del atributo BRA, que no tendrá ningún efecto entre sí.
Sin embargo, este método de implementación aún no es perfecto. Dado que se introduce el constructor, también enfrentamos el problema con el constructor mencionado en el artículo anterior: si hay una definición de método en el constructor, entonces hay una referencia de función separada para ninguna de las instancias. Nuestro propósito es compartir este método, y los métodos que definimos en el prototipo Supertype no se pueden llamar en la instancia de subtipo:
function superclass () {this.name = "Women"; this.bra = ["A", "B"]; } Superclass.prototype.saywhat = function () {console.log ("hola"); } function subclass () {this.subname = "tu hermana"; Superclass.call (esto); } var sub1 = new subclass (); console.log (sub1.saywhat ()); // typeError: Undefined no es una funciónSi ha leído el artículo anterior sobre objetos y constructores prototipo, ¡ya debe saber la respuesta para resolver este problema, es decir, siga la rutina del artículo anterior y use "combinación de combinación"!
Herencia combinada
La herencia combinada es una forma de combinar las ventajas de la cadena prototipo y el constructor, y combinarlas para lograr la herencia. En pocas palabras, es usar la cadena prototipo para heredar atributos y métodos, y usar constructores prestados para implementar la herencia de los atributos de instancia. Esto no solo resuelve el problema del intercambio de atributos de instancia, sino que también permite que se hereden atributos y métodos súper de tipo:
function superclass () {this.name = "Women"; this.bra = ["A", "B"]; } Superclass.prototype.saywhat = function () {console.log ("hola"); } function subclass () {this.subname = "tu hermana"; Superclass.call (esto); // La segunda llamada a SuperClass} subclass.prototype = new SuperClass (); // la primera llamada a superclase var sub1 = new subclass (); console.log (sub1.saywhat ()); //HolaEl método de herencia de combinación también es la forma más utilizada de implementar la herencia en el desarrollo real. En este punto, puede satisfacer sus necesidades de desarrollo reales, pero la búsqueda de la perfección de las personas es interminable, por lo que inevitablemente habrá alguien "encontrar" sobre este patrón: ¡su patrón ha llamado al constructor súper de tipo dos veces! Dos veces. . . Lo hiciste? ¿Es esta amplificación de 100 veces la pérdida de rendimiento?
La refutación más poderosa es encontrar una solución, pero afortunadamente el desarrollador ha encontrado la mejor solución a este problema:
Herencia de combinación parásita
Antes de introducir este método de herencia, primero entendemos el concepto de constructor parásito. El constructor parásito es similar al patrón de fábrica mencionado anteriormente. Su idea es definir una función común. Esta función se usa específicamente para manejar la creación de un objeto. Después de completar la creación, devuelve este objeto. Esta función es muy similar a un constructor, pero el constructor no devuelve un valor:
function gf (name, bra) {var obj = new Object (); obj.name = nombre; obj.bra = sujetador; obj.saywhat = function () {console.log (this.name); } return obj;} var gf1 = new Gf ("bingbing", "c ++"); console.log (gf1.saywhat ()); // bingbingLa implementación de la herencia parásita es similar al constructor parásito. Crea una función "de fábrica" que no depende de tipos específicos, se trata específicamente del proceso de herencia de objetos y luego devuelve la instancia de objeto heredado. Afortunadamente, esto no requiere que lo implementemos nosotros mismos. Dao Ge (Douglas) nos ha proporcionado durante mucho tiempo un método de implementación:
objeto de función (obj) {function f () {} f.prototype = obj; return new f ();} var superclass = {nombre: "bingbing", bra: "c ++"} var subclass = object (superclass); console.log (subclass.name); // bingbingSe proporciona un constructor simple en una función pública, y luego una instancia del objeto pasada se asigna al objeto prototipo del constructor, y finalmente devolver una instancia del constructor es muy simple, pero la eficacia es muy buena, ¿no? Posteriormente, este método se llama "herencia prototipo", y la herencia parásita se logra en función del prototipo al mejorar las propiedades personalizadas del objeto:
función buildobj (obj) {var o = object (obj); o.saywhat = function () {console.log ("hola"); } return o;} var superclass = {nombre: "bingbing", bra: "c ++"} var gf = buildObj (superclass); gf.saywhat (); // holaLa herencia parasitaria también enfrenta el problema de la reutilización de funciones en los prototipos, por lo que las personas comenzaron a reunir los bloques de construcción nuevamente, y nació la herencia de combinación parásita, con el propósito de resolver el problema de llamar al constructor de tipo principal al especificar un prototipo subtipo, y al mismo tiempo, para maximizar la reutilización de la función. Según los métodos de implementación básicos anteriores, son los siguientes:
// Los parámetros son dos constructoras functions inheritobj (sub, sup) {// implementan la herencia de instancias y obtenga una copia del supertipo var proto = objeto (sup.prototype); // Respecificar el atributo constructor de la instancia Proto Proto.Constructor = sub; // Asigna el objeto creado al prototipo del subtipo sub.prototype = proto;} function superclass () {this.name = "Women"; this.bra = ["A", "b"];} superclass.prototype.saywhat = function () {console.log ("hello");} function subclass () {this.name = "women"; this.bra = ["A", "b"];} superclass.prototype.saywhat = function () {console.log ("hello");} function subclass () {this.subname = "tu hermana"; Superclass.call (this);} HerHheritObj (subclase, superclase); var sub1 = new Subclass (); console.log (sub1.saywhat ()); //HolaEsta implementación evita dos llamadas a los supertipos, y también guarda propiedades innecesarias en subclase.prototype, y también mantiene la cadena prototipo. En este punto, el viaje de herencia ha terminado, y esta implementación también se ha convertido en el método de implementación de herencia más ideal. La controversia de las personas sobre la herencia de JavaScript aún continúa. Algunos abogan por OO, y algunos se oponen a hacer esfuerzos innecesarios en JavaScript para realizar las características de OO. ¿Qué pasa si es así, al menos tengo una comprensión más profunda!