Sobes.tech
Junior

Explica cómo funciona la herencia en JavaScript.

sobes.tech AI

Respuesta de la IA

La herencia en JavaScript se implementa a través de prototipos. Cada objeto en JavaScript tiene una propiedad interna [[Prototype]] que hace referencia a otro objeto. Cuando intentamos acceder a una propiedad o método de un objeto que no está directamente en él, el motor de JavaScript busca esa propiedad o método en el prototipo de ese objeto. Si no lo encuentra allí, continúa la búsqueda a lo largo de la cadena de prototipos, que se forma mediante las conexiones [[Prototype]], hasta que alcanza null (el final de la cadena).

Existen dos mecanismos principales de herencia:

  1. Herencia prototípica clásica (ES5 y anteriores): Basada en el uso de funciones constructoras y su propiedad prototype.

    // Función constructora
    function Animal(name) {
      this.name = name;
    }
    
    // Añadir método al prototipo
    Animal.prototype.speak = function() {
      console.log(`${this.name} emite un sonido.`);
    };
    
    // Crear instancia
    const dog = new Animal('Perro');
    
    // Llamar método del prototipo
    dog.speak(); // Imprime: Perro emite un sonido.
    
    // El prototipo de cat es Animal.prototype
    console.log(Object.getPrototypeOf(dog) === Animal.prototype); // true
    
  2. Herencia basada en clases (ES6 y posteriores): Usa la sintaxis class y extends, que internamente todavía opera con prototipos.

    // Clase base
    class Animal {
      constructor(name) {
        this.name = name;
      }
    
      speak() {
        console.log(`${this.name} emite un sonido.`);
      }
    }
    
    // Subclase que hereda de Animal
    class Dog extends Animal {
      constructor(name, breeds) {
        super(name); // Llamada al constructor de la clase padre
        this.breeds = breeds;
      }
    
      bark() {
        console.log(`${this.name} ladra.`);
      }
    }
    
    // Crear instancia de la subclase
    const myDog = new Dog('Bublik', ['Labrador', 'Collie']);
    
    // Llamar métodos de Animal y Dog
    myDog.speak(); // Imprime: Bublik emite un sonido.
    myDog.bark();  // Imprime: Bublik ladra.
    
    // El prototipo de myDog es Dog.prototype
    console.log(Object.getPrototypeOf(myDog) === Dog.prototype); // true
    
    // El prototipo de Dog.prototype es Animal.prototype
    console.log(Object.getPrototypeOf(Dog.prototype) === Animal.prototype); // true
    

Conceptos clave:

  • Cadena de prototipos: La relación entre objetos a través de sus [[Prototype]].

  • __proto__ (obsoleto/no estándar, pero útil para entender): Propiedad mediante la cual se puede acceder a [[Prototype]]. No se recomienda usar en producción.

  • Object.getPrototypeOf(obj): Forma estándar de obtener el prototipo de un objeto.

  • Object.setPrototypeOf(obj, proto): Forma estándar de cambiar el prototipo de un objeto (no recomendable modificar el prototipo de objetos existentes después de su creación).

  • instanceof: Operador para verificar si un objeto es instancia de una clase o función constructora, comprobando su cadena de prototipos.

    console.log(myDog instanceof Dog);   // true
    console.log(myDog instanceof Animal); // true
    console.log(myDog instanceof Object); // true (al final de la cadena Object.prototype)
    
  • super: Se usa en clases para llamar a métodos o al constructor de la clase padre.

La diferencia entre enfoques clásico y basado en clases: Las clases ofrecen una forma más estructurada y sintácticamente cómoda de trabajar con herencia prototípica, similar a otros lenguajes de programación. Sin embargo, el mecanismo fundamental sigue siendo el mismo: herencia a través de prototipos.