Programación Orientada a Objetos y Diferentes Métodos de Herencia

Contenido:

  • Declaración de clases
  • Creación de instancias
  • Varios métodos de herencia

Declaración e Instanciación de Clases

/**
 * Declaración de clase
 */
function Animal(){
    this.nombre = 'nombre';
}
/**
 * Declaración de clase en ES6
 */
class Animal2{
    constructor(){
        this.nombre = 'nombre';
    }
}

Instanciación

Se utiliza el operador new para instanciar

/**
 *
 * Instanciación
 */
new Animal();
new Animal2();

Métodos de Herencia

Uno: Herencia mediante funciones constructoras

Implementación del código:

function Padre(nombre){
    this.nombre = nombre; //Propiedades básicas de la instancia
    this.gustos = ['manzana','plátano']; //Propiedades de referencia de la instancia
    this.comer = function(){ //Funciones de instancia (propiedades de referencia)
        console.log(this.nombre + ' le gusta ' + this.gustos.join(' '));
    };
}
//Métodos del prototipo
Padre.prototype.hablar = function(){
    console.log(this.nombre + ' dice hola');
};
function Hijo(nombre){
    Padre.call(this,nombre); //Punto clave call también puede ser reemplazado por apply
    this.tipo = 'hijo1';
}
var hijo1 = new Hijo('Carlos');
var hijo2 = new Hijo('Marta');
hijo2.gustos.push('naranja');

hijo1.comer(); //Carlos le gusta manzana plátano
hijo2.comer(); //Marta le gusta manzana plátano naranja
hijo1.hablar(); //hijo1.hablar is not a function

Principio de implementación de la herencia: Ejecutar Padre.call(this) en el constructor de la clase hija Hijo; es decir, ejecutar el constructor de la clase padre Padre en el constructor de la clase hija Hijo, cambiando al mismo tiempo el contexto de ejecución (this apunta a Hijo). De esta manera, las propiedades de instancia de la clase padre se montarán en la clase hija, logrando así la herencia. Es como copiar las propiedades y métodos de instancia del padre para darlos a la instancia de la hija.

Desventajas de este método de herencia:

1. Solo se pueden heredar las propiedades y métodos de instancia de la clase padre, no las propiedades y métodos del prototipo de la clase padre. Por lo tanto, en el ejemplo anterior, hijo1 no puede obtener el método say

2. No se puede lograr el compartir funciones, cada vez que se usa new, las propiedades y métodos de Hijo crearán un espacio de memoria, lo que es un desperdicio de memoria y carece de eficiencia.

Para resolver el problema del consumo de memoria, se presenta a continuación la herencia de cadena de prototipos

Dos: Herencia de Cadena de Prototipos

Principio de implementación: Cada función constructora tiene una propiedad prototype que apunta al prototipo de esa función constructora (objeto prototipo), y las propiedades y métodos de este prototipo (objeto prototipo) serán heredados por las instancias de esa función constructora.

El código es el siguiente:

function Padre(nombre){
    this.nombre = nombre; //Propiedades básicas de la instancia
    this.gustos = ['manzana','plátano']; //Propiedades de referencia de la instancia
}
//Métodos del prototipo
Padre.prototype.comer = function(){
    console.log(this.nombre + ' le gusta ' + this.gustos.join(' '));
};
function Hijo(nombre){
    this.tipo = 'hijo1';
}
Hijo.prototype = new Padre();//Punto clave
Hijo.prototype.constructor = Hijo;//Corregir la apuntación del constructor

var hijo1 = new Hijo('Carlos');
var hijo2 = new Hijo('Marta');
hijo2.gustos.push('naranja');

hijo1.comer(); //undefined le gusta manzana plátano naranja
hijo2.comer(); //undefined le gusta manzana plátano naranja

La herencia de cadena de prototipos resuelve el problema del consumo de memoria de la herencia del constructor, pero también presenta nuevos problemas:

Desventajas de este método de herencia:

1. Al crear instancias de la clase hija, no se pueden pasar parámetros al constructor de la clase padre (en el ejemplo anterior, el nombre en Hijo no se puede pasar a Padre).

2. Si se modifica una propiedad en la instancia, la propiedad del prototipo también se modificará, y todas las propiedades de las clases que hereden de este prototipo cambiarán juntas.

En el código anterior, solo se modificó la propiedad gustos de la instancia hijo2, porque esta propiedad es una propiedad de su prototipo, es decir, se modificó la propiedad del prototipo, por lo que el valor de la instancia hijo1 también cambió.

Tres: Método Combinado

Tomando prestadas las ventajas y desventajas de los dos métodos anteriores, se utiliza un método que combina el constructor y la cadena de prototipos

El código es el siguiente:

function Padre(nombre){
    this.nombre = nombre; //Propiedades básicas de la instancia
    this.gustos = ['manzana','plátano']; //Propiedades de referencia de la instancia
}
//Métodos del prototipo
Padre.prototype.comer = function(){
    console.log(this.nombre + ' le gusta ' + this.gustos.join(' '));
};
function Hijo(nombre){
    Padre.call(this,nombre); //Punto clave
    this.tipo = 'hijo1';
}
Hijo.prototype = new Padre();//Punto clave El prototipo de la clase hija apunta a la clase padre
Hijo.prototype.constructor= Hijo;//Corregir la apuntación del constructor

var hijo1 = new Hijo('Carlos');
var hijo2 = new Hijo('Marta');
hijo2.gustos.push('naranja');

hijo1.comer(); //Carlos le gusta manzana plátano
hijo2.comer(); //Marta le gusta manzana plátano naranja

Principio de implementación:

A través de Padre.call(this,nombre); se pueden heredar las propiedades básicas y de referencia del padre, y se pueden pasar parámetros;

A través de Hijo.prototype = new Padre(); el prototipo de la clase hija apunta a la instancia de la clase padre, de esta manera la instancia de la clase hija (es decir, new hijo()) hereda todas las propiedades y métodos del padre (incluyendo propiedades/métodos de instancia y propiedades/métodos del prototipo).

Ventajas:

1. Al modificcar las propiedades de la instancia de la clase hija, no se provocará cambios en las propiedades de la clase padre.

2. Ya se pueden pasar parámetros al padre.

Desventajas:

1. Al crear instancias de la clase hija, el constructor del padre se ejecuta dos veces.

En el código anterior, ya se ejecutó una vez Padre en la herencia del constructor (es decir, Padre.call(this,nombre)), las propiedades del padre Padre ya existen en la clase hija Hijo (Hijo ya tiene las propiedades y métodos de Padre, excepto las propiedaeds del prototipo). Entonces, en la herencia de la cadena de prototipos (es decir, Hijo.prototype = new Padre()), no es necesario ejecutar Padre nuevamente, solo se necesitan heredar las propiedades y métodos del prototipo.

Cuatro: Optimización del Método Combinado 1 (Recomendado)

En el método combinado, para resolver las desventajas de la herencia del constructor (es decir, que no se pueden heredar las propiedades del prototipo del padre), el prototipo de la clase hija se apunta a la instancia del padre. Pero las propiedades del padre ya existen en la clase hija durante la herencia del constructor, la clase hija solo le faltan las propiedades y métodos del prototipo. Por lo tanto, optimizamos aún más, haciendo que el prototipo de la clase hija apunte al prototipo del padre. Así se evita la segunda ejecución del constructor del padre.

El código es el siguiente:

function Padre(nombre){
    this.nombre = nombre; //Propiedades básicas de la instancia
    this.gustos = ['manzana','plátano']; //Propiedades de referencia de la instancia
}
//Métodos del prototipo
Padre.prototype.comer = function(){
    console.log(this.nombre + ' le gusta ' + this.gustos.join(' '));
};
function Hijo(nombre){
    Padre.call(this,nombre); //Punto clave
    this.tipo = 'hijo1';
}
Hijo.prototype = Padre.prototype;//Punto clave El prototipo de la clase hija apunta al prototipo del padre
Hijo.prototype.constructor= Hijo;//Corregir la apuntación del constructor

var hijo1 = new Hijo('Carlos');
var hijo2 = new Hijo('Marta');
hijo2.gustos.push('naranja');

hijo1.comer(); //Carlos le gusta manzana plátano
hijo2.comer(); //Marta le gusta manzana plátano naranja

Desventajas:

En el código anterior, Hijo.prototype = Padre.prototype; es decir, el prototipo de la clase hija apunta al prototipo del padre, en este momento el constructor del prototipo de la clase hija es el constructor del prototipo del padre, y el constructor del prototipo del padre es Padre. Obviamente, esto no es lo que queremos, por lo que ejecutamos Hijo.ptototype.constructor = Hijo; es decir, el constructor del prototipo de la clase hija apunta a sí mismo. Pero esto también hace que el constructor del padre apunte a la clase hija. Como en el siguiente código:

console.log(hijo1.__proto__.constructor ===  Hijo); //true
console.log( new Padre().__proto__.constructor ===  Hijo);//true
console.log( new Padre().__proto__.constructor ===  Padre);//false

A través del código anterior, podemos ver que el constructor de la instancia de la clase hija es Hijo; el constructor de la instancia de la clase padre también es Hijo.

Cinco: Optimización del Método Combinado 2 (Recomendado en ES5)

Principio de implementación: A través del método Object.crete(obj), este método crea un nuevo objeto y este nuevo objeto hereda el objeto especificado obj. Similar a:

//Implementar el principio de Object.create() en una función
function crear(obj){
    function F(){}; //Crear una función constructora
    F.prototype = obj; //Asignar el objeto obj al prototipo de F
    return new F(); //Devolver la instancia de la función constructora, así la instancia de F heredará todas las propiedades y métodos de obj
}

Implementación del código:

function Padre(nombre){
    this.nombre = nombre; //Propiedades básicas de la instancia
    this.gustos = ['manzana','plátano']; //Propiedades de referencia de la instancia
}
//Métodos del prototipo
Padre.prototype.comer = function(){
    console.log(this.nombre + ' le gusta ' + this.gustos.join(' '));
};
function Hijo(nombre){
    Padre.call(this,nombre); //Punto clave
    this.tipo = 'hijo1';
}
Hijo.prototype = Object.create(Padre.prototype);//Punto clave El prototipo de la clase hija apunta al objeto creado por Object.create
Hijo.prototype.constructor = Hijo;//Apuntar el constructor del prototipo de Hijo a Hijo

var hijo1 = new Hijo('Carlos');
var hijo2 = new Hijo('Marta');
hijo2.gustos.push('naranja');

hijo1.comer(); //Carlos le gusta manzana plátano
hijo2.comer(); //Marta le gusta manzana plátano naranja
console.log(hijo1.__proto__.constructor ===  Hijo); //true
console.log(new Padre().__proto__.constructor ===  Padre);//true

El código Hijo.prototype = Object.create(Padre.prototype); es decir, el prototipo de la clase hija apunta al nuevo objeto creado por Object.create(Padre.prototyep), este objeto hereda todas las propiedades y métodos de Padre.prototype. Así el constructor de la clase hija y el constructor de la clase padre se separan. Luego, el constructor del prototipo de la clase hija se apunta a sí mismo.

Por el código anterior, podemos ver que el constructor de la instancia de la clase hija apunta a la clase hija Hijo, y el constructor de la instancia de la clase padre apunta a la clase padre Padre. Este es exactamente el resultado que queremos.

Seis: Herencia con class y extends en ES6 (Recomendado)

          class Padre {
                constructor(nombre){
                    this.nombre = nombre
                    this.gustos = ['manzana','plátano'] 
                }
                comer(){
                    console.log(this.nombre + ' le gusta ' + this.gustos.join(' '))
                }
            }

            class Hijo extends Padre{
                constructor(nombre){
                    super(nombre)
                }
            }
            var hijo1 = new Hijo('Carlos');
            var hijo2 = new Hijo('Marta');
            hijo2.gustos.push('naranja');
            hijo1.comer(); //Carlos le gusta manzana plátano
            hijo2.comer(); //Marta le gusta manzana plátano naranja
            console.log(hijo1.__proto__.constructor ===  Hijo); //true
            console.log(new Padre().__proto__.constructor ===  Padre);//true

Utilizando la herramienta babel para convertir, descubriremos que extends realmente utiliza el método de herencia combinada parasitaria, por lo que también se demuestra que este es un método de solución de herencia más óptimo.

Etiquetas: programación-orientada-a-objetos herencia ecmascript JavaScript clases

Publicado el 6-16 17:07