Herencia entre clases y el uso de extends y super() en JavaScript

Dominar la herencia es dar el salto de escribir código repetitivo a crear arquitecturas robustas y escalables. En esta guía, exploraremos cómo las palabras clave extends y super() actúan como los cimientos que permiten a tus clases comunicarse y compartir lógica de forma eficiente.

Herencia entre clases y el uso de extends y super() en JavaScript

1. ¿Qué es la herencia entre clases?

La herencia entre clases en JavaScript es un mecanismo que permite que una clase (hija) reutilice las propiedades y métodos de otra clase (padre).

Esto significa que una clase puede extender el comportamiento de otra, evitando repetir código y permitiendo construir estructuras más organizadas y escalables.

En otras palabras:

  • Clase padre (base) → define comportamiento general.
  • Clase hija (derivada) → hereda ese comportamiento y puede usarlo o modificarlo.

Este concepto es fundamental cuando un proyecto crece, porque permite crear arquitecturas reutilizables.

CONCEPTO padre–hijo

La relación funciona así:

ClasePadre
   ↑
ClaseHija

La clase hija hereda todo lo público de la clase padre.

Esto incluye:

  • propiedades
  • métodos
  • lógica reutilizable

RECUERDA! – El prototipo por detrás:
Aunque usamos la sintaxis de class (introducida en ES6), es importante recordar que JavaScript sigue usando herencia prototípica. Las clases son «azúcar sintáctico»; por debajo, el motor de JS conecta los prototipos de los objetos para que puedan buscar métodos en la cadena de mando.

1.1. Ejemplo simple de herencia

Supongamos que tenemos una clase base que representa un componente del DOM.

// --- CLASE PADRE ----
class ComponenteUI {

  constructor(elemento) {

    // Guardamos una referencia a un elemento del DOM
    this.elemento = elemento
  }

  // Método que muestra el elemento
  mostrar() {

    // Cambia el display del elemento
    this.elemento.style.display = "block"
  }

  // Método que oculta el elemento
  ocultar() {

    // Oculta el elemento
    this.elemento.style.display = "none"
  }
}Lenguaje del código: JavaScript (javascript)

¿Qué representa esta clase?

Esta clase define comportamientos comunes para elementos del DOM, como:

  • mostrar
  • ocultar

Ahora podemos crear otras clases más específicas.

📌AHORA CREAMOS UNA CLASE HIJA CON extends

//    --- CLASE HIJA ----   Que hereda de ComponenteUI
class Modal extends ComponenteUI {

  abrir() {

    // usamos el método heredado de la clase padre
    this.mostrar()

    console.log("Modal abierto")
  }

  cerrar() {

    // usamos el método heredado
    this.ocultar()

    console.log("Modal cerrado")
  }
}Lenguaje del código: JavaScript (javascript)

Qué ocurre aquí

La clase Modal hereda automáticamente de la clase padre, la clase ComponenteUI. (para ello usamos extends ampliaremos -):

mostrar()
ocultar()
elemento

1.1.1. Uso real en el DOM

Supongamos que tenemos este HTML:

<div id="modal">Contenido del modal</div>Lenguaje del código: HTML, XML (xml)

Ahora lo usamos con nuestras clases.

// seleccionamos el elemento del DOM
const modalElemento = document.getElementById("modal")

// creamos una instancia de la clase Modal
const modal = new Modal(modalElemento)

// abrimos el modal
modal.abrir()

// cerramos el modal
modal.cerrar()Lenguaje del código: JavaScript (javascript)

Qué sucede aquí

  • Modal hereda la lógica de ComponenteUI
  • reutiliza los métodos mostrar() y ocultar()
  • agrega su propia lógica (abrir y cerrar)

Esto evita repetir código.

1.2. Idea clave que debes recordar de este punto

La herencia permite que:

  1. Una clase especializad – Herede comportamiento – De una clase más general

Ejemplo conceptual:

ComponenteUI
      ↑
    Modal
      ↑
  ModalLogin

Cada nivel hereda y especializa comportamiento.

EN RESUMEN:
La herencia entre clases en JavaScript permite que una clase hija reutilice propiedades y métodos de una clase padre mediante extends. Esto se usa en proyectos reales para crear componentes reutilizables del DOM, organizar la arquitectura del código y evitar duplicación de lógica.

2. extends

2.1. ¿Qué es extends en JavaScript?

extends es una palabra clave de JavaScript que se usa para crear una clase hija a partir de una clase padre.

Permite que la nueva clase herede propiedades y métodos de la clase base.

Esto forma una relación llamada herencia.

2.2. Sintaxis general de extends

La sintaxis básica es la siguiente:

class ClasePadre {

  // propiedades y métodos generales

}

class ClaseHija extends ClasePadre {

  // propiedades y métodos específicos

}Lenguaje del código: JavaScript (javascript)

Aquí ocurre lo siguiente:

  • ClaseHija hereda todo lo público de ClasePadre
  • ClaseHija puede usar esos métodos directamente

2.3. Ejemplo simple paso a paso

Primero creamos una clase padre.

// Clase padre
class Animal {

  constructor(nombre) {

    // Guardamos el nombre del animal
    this.nombre = nombre
  }

  // Método que describe una acción
  hacerSonido() {

    console.log("El animal hace un sonido")
  }

}Lenguaje del código: JavaScript (javascript)

Qué tiene esta clase

  • Propiedad: nombre
  • Método: hacerSonido()

Ahora creamos una clase hija usando extends.

// Clase hija que hereda de Animal
class Perro extends Animal {

  ladrar() {

    // método propio de la clase Perro
    console.log(this.nombre + " está ladrando")

  }

}Lenguaje del código: JavaScript (javascript)

Qué ocurre aquí

Perro hereda automáticamente de Animal:

nombre
hacerSonido()

Y además tiene su propio método:

ladrar()

2.3.1. Uso de la clase hija

Ahora creamos un objeto de la clase Perro.

// Creamos una instancia de la clase Perro
const miPerro = new Perro("Rex")

// usamos el método heredado de la clase padre
miPerro.hacerSonido()
   // RESULTADO: El animal hace un sonido

// usamos el método propio de la clase hija
miPerro.ladrar()
   // RESULTADO: Rex está ladrandoLenguaje del código: JavaScript (javascript)

¿Qué pasó internamente?

Cuando usamos:

class Perro extends AnimalLenguaje del código: JavaScript (javascript)

JavaScript crea una relación de herencia como esta:

Perro
   ↓
Animal

Entonces cuando hacemos:

miPerro.hacerSonido()Lenguaje del código: CSS (css)

JavaScript busca el método en este orden:

objeto miPerroPerro.prototypeAnimal.prototypeLenguaje del código: CSS (css)

Si lo encuentra en la cadena, lo ejecuta.

Esto se llama cadena prototípica.

2.4. ¿Qué hereda una clase hija cuando usamos extends?

Esto incluye principalmente:

  1. Métodos definidos en el prototype de la clase padre
  2. La estructura del constructor (cuando se usa con super())
  3. Acceso a las propiedades que inicializa la clase padre

Esto permite reutilizar lógica sin duplicar código.

3. super()

3.1. ¿Qué es super() en JavaScript?

super es una herramienta dual. Dependiendo de cómo la uses (como una función o como un objeto), te da acceso a diferentes partes del padre:

  1. Para heredar las propiedades del padre (Como función)
  2. Para llamar a un método que existe en el padre (Como objeto)

En este punto nos centraremos para heredar las propiedades del padre

En otras palabras, super() permite que la clase hija inicialice las propiedades que pertenecen a la clase padre.

Cuando usamos herencia:

class Hija extends PadreLenguaje del código: JavaScript (javascript)

la clase hija debe ejecutar primero el constructor del padre para que el objeto se cree correctamente.

Para eso se usa: super()

3.2. Qué hace realmente super()

Cuando se ejecuta super() ocurre lo siguiente:

  1. Se ejecuta el constructor de la clase padre
  2. Se inicializan las propiedades definidas en el padre
  3. Se crea correctamente el objeto (this)
  4. El flujo vuelve al constructor de la clase hija

3.3. ¿Como usar super() para heredar propiedades?

Teniendo en mente su sintaxis:

super(argumento1, argumento2, ...)

Se utiliza únicamente dentro del constructor de la clase hija. Su función es llamar al constructor de la clase padre y enviarle los datos que necesita para inicializarse.

Reglas de oro para su uso correcto:

  1. Debe ser la primera línea:
    • No puedes usar this antes de llamar a super().
  2. Correspondencia:
    • Los argumentos que pongas dentro de super() deben coincidir con lo que el constructor del padre espera recibir.

3.4. Ejemplo sencillo

Imagina que tenemos una clase base Empleado que solo guarda el nombre, y una clase Vendedor que además necesita guardar cuánto ha vendido.

// CLASE PADRE 
class Empleado {
  // Aquí el constructor espera recibir 1 argumento (nombre)
  constructor(nombre) {
    this.nombre = nombre; // El padre sabe cómo guardar el nombre
  }
}

// CLASE HIJA 
class Vendedor extends Empleado {
  constructor(nombre, ventasTotales) {
    // PASO 1: Le enviamos el 'nombre' al argumento del padre para que él lo gestione
    super(nombre); 
    
    // PASO 2: Ahora el hijo guarda lo que le es propio
    this.ventasTotales = ventasTotales;
  }

  mostrarInfo() {
    console.log(`${this.nombre} ha vendido $${this.ventasTotales}.`);
  }
}

const miVendedor = new Vendedor("Ana", 5000);
miVendedor.mostrarInfo(); // Salida: Ana ha vendido $5000.
Lenguaje del código: JavaScript (javascript)

Explicación del Proceso

  1. constructor(nombre, ventasTotales):
    • El Vendedor recibe dos datos cuando lo creas con new.
  2. super(nombre): Aquí es donde ocurre la magia. El Vendedor le dice al Empleado: «Oye, toma este nombre y guárdalo tú, que tú ya sabes cómo hacerlo».
    • Esto ejecuta el código this.nombre = nombre que está definido en la clase padre.
  3. this.ventasTotales = ventasTotales:
    • Una vez que el padre terminó su trabajo, el hijo puede seguir con sus propias propiedades únicas.

¿Qué pasa si olvidas el argumento?

Si escribes solo super(), el constructor del padre se ejecutará, pero el nombre será undefined, porque no le pasaste la información necesaria.

3.5. ¿Por qué super() es obligatorio?

En una clase hija no se puede usar this hasta que el constructor del padre haya sido ejecutado.

Esto ocurre porque la clase padre es la responsable de inicializar el objeto base.

Por eso JavaScript exige que super() se ejecute primero.

3.6. Error común: usar this antes de super()

Este código es incorrecto.

class Estudiante extends Persona {

  constructor(nombre, curso) {

    // ❌ ERROR
    this.curso = curso

    // el constructor del padre aún no se ejecutó
    super(nombre)

  }

}Lenguaje del código: JavaScript (javascript)

JavaScript lanzará un error como:

ReferenceError:
Must call super constructor before using 'this'Lenguaje del código: JavaScript (javascript)

Esto ocurre porque el objeto aún no ha sido inicializado por el padre.

IDEA CLAVE QUE DEBEMOS RECORDAR SOBRE super()

super() sirve para:

  • Ejecutar el constructor del padre
  • Inicializar propiedades heredadas
  • Crear correctamente el objeto (this)

Sin super(): La clase hija no puede usar this

4. super.metodo()

4.1. ¿Qué es super.metodo() en JavaScript?

super.metodo() es una forma de acceder y ejecutar un MÉTODO definido en la clase padre desde una clase hija.

Se usa principalmente cuando:

  • Queremos reutilizar la lógica del método del padre
  • Queremos extender ese comportamiento en la clase hija

En otras palabras:

super.metodo() ejecuta el método que está en la clase padre

4.2. Diferencia entre super() y super.metodo()

Es importante no confundirlos.

UsoFunción
super()ejecuta el constructor de la clase padre
super.metodo()ejecuta un método del padre

Ejemplo conceptual:

super()        // constructor del padre
super.saludar() // método del padreLenguaje del código: JavaScript (javascript)

4.3. Ejemplo básico

1️⃣Primero creamos una clase padre.

// Clase padre
class Persona {

  constructor(nombre) {

    // propiedad que guarda el nombre
    this.nombre = nombre

  }

  saludar() {

    console.log("Hola, soy " + this.nombre)

  }

}Lenguaje del código: JavaScript (javascript)

Esta clase tiene:

propiedad → nombre
método → saludar()

2️⃣Ahora creamos una clase hija que usa super.metodo()

// Clase hija que hereda de Persona
class Estudiante extends Persona {

  constructor(nombre, curso) {

    // ejecuta el constructor del padre
    super(nombre)

    // propiedad propia del estudiante
    this.curso = curso

  }

  saludar() {

    // usamos el método de la clase padre
    super.saludar()

    // agregamos comportamiento adicional
    console.log("Estoy estudiando " + this.curso)

  }

}Lenguaje del código: JavaScript (javascript)

¿Qué está pasando en este código?

El método saludar() existe en el padre, pero en la clase hija lo sobrescribimos.

Dentro de ese método hacemos:

super.saludar()Lenguaje del código: JavaScript (javascript)

Eso ejecuta el método original del padre.

Luego agregamos comportamiento nuevo.

3️⃣Uso del código

// creamos una instancia
const alumno = new Estudiante("Laura", "JavaScript")

// llamamos al método saludar
alumno.saludar()Lenguaje del código: JavaScript (javascript)

Resultado:

Hola, soy Laura
Estoy estudiando JavaScript

Primero se ejecuta el método del padre y luego el del hijo.

5. El polimorfismo

5.1. ¿Qué es el polimorfismo?

El polimorfismo es uno de los pilares de la programación orientada a objetos. Su nombre viene del griego «muchas formas».

En JavaScript, se refiere a la capacidad de que diferentes clases tengan un método con el mismo nombre, pero que cada una lo ejecute de una manera distinta.

5.2. ¿Cómo funciona en JavaScript?

En JS, el polimorfismo suele ocurrir a través de la herencia. Una clase padre define un método general, y las clases hijas lo «sobrescriben» para darle su propio comportamiento.

Lo más potente es que puedes tratar a diferentes objetos de forma uniforme (llamar al mismo método) sin preocuparte de qué tipo exacto son.

5.3. Ejemplo simple con clases

5.3.1. Primero creamos una clase base.

// Clase base
class Animal {

  sonido() {

    // comportamiento general
    console.log("El animal hace un sonido")

  }

}Lenguaje del código: JavaScript (javascript)

Esta clase define un método llamado:

sonido()

5.3.2. Crear clases hijas con comportamientos distintos

Ahora creamos distintas clases que heredan de Animal.

// Clase hija Perro
class Perro extends Animal {

  sonido() {

    // sobrescribimos el método sonido()
    console.log("El perro ladra")

  }

}

// Clase hija Gato
class Gato extends Animal {

  sonido() {

    // mismo método, comportamiento diferente
    console.log("El gato maúlla")

  }

}

// Clase hija Pájaro
class Pajaro extends Animal {

  sonido() {

    console.log("El pájaro canta")

  }

}Lenguaje del código: JavaScript (javascript)

Todas tienen el mismo método:

sonido()

Pero cada clase lo implementa de manera distinta.

5.3.3. Creamos objetos

// creamos distintos objetos

const perro = new Perro()
const gato = new Gato()
const pajaro = new Pajaro()Lenguaje del código: JavaScript (javascript)

5.3.4. Ejecutar el mismo método en distintos objetos

// llamamos al mismo método en distintos objetos

perro.sonido()
gato.sonido()
pajaro.sonido()Lenguaje del código: JavaScript (javascript)

Resultado:

El perro ladra
El gato maúlla
El pájaro canta

Aquí vemos claramente el polimorfismo.

El método es el mismo:

sonido()

Pero el comportamiento cambia según el objeto.

5.3.5. Ejemplo clásico de polimorfismo

Podemos usar un array de objetos distintos y ejecutar el mismo método.

// creamos un array con distintos animales
const animales = [
  new Perro(),
  new Gato(),
  new Pajaro()
]

// recorremos el array
animales.forEach(animal => {

  // llamamos al mismo método
  animal.sonido()

})Lenguaje del código: JavaScript (javascript)

Resultado:

El perro ladra
El gato maúlla
El pájaro canta

La función no necesita saber qué tipo de objeto es.

Solo sabe que tiene un método:

sonido()

5.4. ¿Por qué es importante?

  1. Flexibilidad:
    • Puedes añadir una nueva clase (por ejemplo, Vaca) con su propio sonido() y el resto de tu código seguirá funcionando sin cambiar ni una sola línea.
  2. Simplicidad:
    • No necesitas llenar tu código de if (esPerro) o if (esGato). Simplemente llamas al método y dejas que el objeto se encargue.
  3. Desacoplamiento:
    • Tu programa principal no necesita conocer los detalles internos de cada clase, solo necesita saber qué «mensajes» (métodos) pueden recibir.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *