JavaScript

JavaScript

Tutorial JavaScript: Herencia

JavaScript herencia: clases y ejemplos claros. Aprende a implementar herencia en clases de JavaScript con ejemplos claros y detallados.

Aprende JavaScript y certifícate

Fundamentos de la herencia en JavaScript: Prototipos vs. clases y el sistema de cadena de prototipos

JavaScript implementa la herencia de una manera única entre los lenguajes de programación populares. A diferencia de lenguajes como Java o C++ que utilizan un sistema de clases tradicional, JavaScript se basa fundamentalmente en prototipos para implementar la herencia, incluso cuando utilizamos la sintaxis moderna de clases introducida en ES6.

El sistema basado en prototipos

En JavaScript, cada objeto está vinculado a otro objeto llamado su prototipo. Cuando intentamos acceder a una propiedad o método que no existe en un objeto, JavaScript automáticamente busca en su prototipo, y si no lo encuentra allí, continúa buscando en el prototipo del prototipo, formando lo que se conoce como cadena de prototipos.

Esta búsqueda continúa hasta llegar al final de la cadena, que normalmente es Object.prototype, cuyo prototipo es null:

// Creación de un objeto usando la notación literal
const animal = {
  estaVivo: true,
  comer() {
    return "Comiendo...";
  }
};

// Creación de un objeto que hereda de animal
const perro = Object.create(animal);
perro.ladrar = function() {
  return "Guau!";
};

console.log(perro.estaVivo); // true (heredado de animal)
console.log(perro.comer());  // "Comiendo..." (heredado de animal)
console.log(perro.ladrar()); // "Guau!" (propio de perro)

En este ejemplo, perro hereda propiedades y métodos de animal a través de la cadena de prototipos. Cuando accedemos a perro.estaVivo, JavaScript no encuentra esta propiedad en perro, por lo que busca en su prototipo (animal) y la encuentra allí.

Accediendo a los prototipos

Podemos acceder y manipular el prototipo de un objeto de varias formas:

  • Object.getPrototypeOf(): Método recomendado para obtener el prototipo de un objeto.
  • Object.setPrototypeOf(): Permite cambiar el prototipo de un objeto existente (no recomendado por razones de rendimiento).
  • proto: Propiedad de acceso obsoleta pero ampliamente soportada.
// Verificando la cadena de prototipos
console.log(Object.getPrototypeOf(perro) === animal); // true
console.log(Object.getPrototypeOf(animal) === Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype) === null); // true

Funciones constructoras y prototipos

Antes de ES6, la forma estándar de implementar herencia era mediante funciones constructoras y la propiedad prototype:

// Función constructora
function Animal(nombre) {
  this.nombre = nombre;
}

// Añadir métodos al prototipo
Animal.prototype.comer = function() {
  return `${this.nombre} está comiendo`;
};

// Función constructora que hereda de Animal
function Perro(nombre, raza) {
  // Llamar al constructor padre
  Animal.call(this, nombre);
  this.raza = raza;
}

// Establecer la herencia
Perro.prototype = Object.create(Animal.prototype);
// Restaurar el constructor
Perro.prototype.constructor = Perro;

// Añadir métodos específicos
Perro.prototype.ladrar = function() {
  return `${this.nombre} dice: Guau!`;
};

const miPerro = new Perro("Rex", "Pastor Alemán");
console.log(miPerro.nombre); // "Rex"
console.log(miPerro.comer()); // "Rex está comiendo"
console.log(miPerro.ladrar()); // "Rex dice: Guau!"

En este ejemplo, Perro hereda de Animal mediante la manipulación de la propiedad prototype. Cuando creamos una instancia con new Perro(), el objeto resultante tiene acceso a los métodos definidos en Perro.prototype y, a través de la cadena de prototipos, a los métodos definidos en Animal.prototype.

Clases en ES6: Azúcar sintáctico

Con ES6, JavaScript introdujo la sintaxis de clases, que proporciona una forma más clara y familiar de implementar la herencia orientada a objetos. Sin embargo, es importante entender que esto es principalmente azúcar sintáctico sobre el sistema de prototipos existente:

// Definición de clase base
class Animal {
  constructor(nombre) {
    this.nombre = nombre;
  }
  
  comer() {
    return `${this.nombre} está comiendo`;
  }
}

// Clase que extiende de Animal
class Perro extends Animal {
  constructor(nombre, raza) {
    super(nombre); // Llama al constructor de Animal
    this.raza = raza;
  }
  
  ladrar() {
    return `${this.nombre} dice: Guau!`;
  }
}

const miPerro = new Perro("Rex", "Pastor Alemán");
console.log(miPerro.comer()); // "Rex está comiendo"
console.log(miPerro.ladrar()); // "Rex dice: Guau!"

Bajo el capó, JavaScript sigue utilizando prototipos. La palabra clave extends configura la cadena de prototipos automáticamente, y super() llama al constructor de la clase padre.

Diferencias clave entre prototipos y clases

Aunque las clases de ES6 se basan en prototipos, existen algunas diferencias importantes:

  • Hoisting: Las clases no se elevan (hoist), mientras que las funciones constructoras sí.
  • Modo estricto: El código dentro de las clases se ejecuta automáticamente en modo estricto.
  • Métodos: Los métodos definidos en clases no son enumerables por defecto.
  • Constructor: Las clases requieren el uso de new, mientras que las funciones constructoras pueden llamarse sin new (aunque no es recomendable).
// Esto funciona con funciones constructoras
const animal1 = Animal("Simba");

// Esto lanzaría un error: las clases deben llamarse con new
// const animal2 = Animal("Simba");

// Correcto
const animal3 = new Animal("Simba");

Verificación de la cadena de prototipos

JavaScript proporciona métodos para verificar las relaciones de herencia:

const miPerro = new Perro("Rex", "Pastor Alemán");

// Verificar si un objeto es instancia de una clase
console.log(miPerro instanceof Perro);       // true
console.log(miPerro instanceof Animal);      // true
console.log(miPerro instanceof Object);      // true

// Verificar si una propiedad pertenece directamente al objeto
console.log(miPerro.hasOwnProperty("nombre")); // true
console.log(miPerro.hasOwnProperty("comer"));  // false (está en el prototipo)

Ventajas del sistema de prototipos

El sistema de prototipos de JavaScript ofrece algunas ventajas interesantes:

  • Flexibilidad: Permite modificar el comportamiento de objetos existentes en tiempo de ejecución.
  • Memoria eficiente: Los métodos se definen una vez en el prototipo y son compartidos por todas las instancias.
  • Herencia dinámica: La cadena de prototipos puede modificarse después de la creación del objeto.
// Extender el prototipo de objetos nativos (no recomendado en código de producción)
String.prototype.saludar = function() {
  return `¡Hola, ${this}!`;
};

console.log("mundo".saludar()); // "¡Hola, mundo!"

El sistema de prototipos de JavaScript proporciona una base poderosa y flexible para la herencia, mientras que la sintaxis de clases moderna ofrece una interfaz más limpia y familiar para los desarrolladores que provienen de otros lenguajes orientados a objetos. Comprender ambos enfoques y cómo se relacionan es fundamental para dominar la programación orientada a objetos en JavaScript.

Implementación práctica de jerarquías de herencia: Extensión de clases, super() y polimorfismo

Una vez comprendidos los fundamentos de la herencia basada en prototipos, es momento de explorar cómo implementar jerarquías de herencia efectivas en JavaScript moderno. La sintaxis de clases introducida en ES6 facilita enormemente la creación de estructuras de herencia claras y mantenibles.

Extensión de clases con extends

La palabra clave extends nos permite crear una clase hija que hereda propiedades y métodos de una clase padre:

class Vehicle {
  constructor(make, model, year) {
    this.make = make;
    this.model = model;
    this.year = year;
    this.isRunning = false;
  }

  start() {
    this.isRunning = true;
    return `The ${this.make} ${this.model} is now running`;
  }

  stop() {
    this.isRunning = false;
    return `The ${this.make} ${this.model} has stopped`;
  }

  getInfo() {
    return `${this.year} ${this.make} ${this.model}`;
  }
}

class Car extends Vehicle {
  constructor(make, model, year, doors) {
    super(make, model, year);
    this.doors = doors;
    this.type = "car";
  }
  
  honk() {
    return "Beep beep!";
  }
}

En este ejemplo, Car extiende de Vehicle, heredando todas sus propiedades y métodos. Además, añade sus propias características específicas como el método honk() y la propiedad doors.

El papel crucial de super()

La palabra clave super tiene dos usos fundamentales en la herencia:

  • Llamar al constructor padre: super() en el constructor
  • Acceder a métodos del padre: super.nombreMetodo() en cualquier método
class ElectricCar extends Car {
  constructor(make, model, year, doors, batteryCapacity) {
    // Llamada al constructor de la clase padre
    super(make, model, year, doors);
    
    this.batteryCapacity = batteryCapacity;
    this.fuelType = "electricity";
  }
  
  // Sobrescribir un método del padre
  start() {
    // Llamar a la implementación del padre
    const message = super.start();
    return `${message} silently`;
  }
  
  charge() {
    return `Charging the ${this.make} ${this.model}'s ${this.batteryCapacity}kWh battery`;
  }
}

const tesla = new ElectricCar("Tesla", "Model 3", 2023, 4, 75);
console.log(tesla.start()); // "The Tesla Model 3 is now running silently"
console.log(tesla.charge()); // "Charging the Tesla Model 3's 75kWh battery"

Es obligatorio llamar a super() antes de usar this en el constructor de una clase hija. Esto inicializa correctamente la instancia con las propiedades definidas en la clase padre.

Errores comunes con super()

class Motorcycle extends Vehicle {
  constructor(make, model, year, engineSize) {
    // Error: No se puede usar 'this' antes de llamar a super()
    this.engineSize = engineSize;
    super(make, model, year);
  }
}

// Correcto:
class Motorcycle extends Vehicle {
  constructor(make, model, year, engineSize) {
    super(make, model, year);
    this.engineSize = engineSize;
  }
}

Implementando polimorfismo

El polimorfismo permite que objetos de diferentes clases respondan al mismo método de manera distinta. En JavaScript, esto se logra principalmente sobrescribiendo métodos de la clase padre:

class Vehicle {
  constructor(make, model) {
    this.make = make;
    this.model = model;
  }
  
  getDescription() {
    return `Vehicle: ${this.make} ${this.model}`;
  }
  
  getFuelEfficiency() {
    return "Varies by vehicle type";
  }
}

class Car extends Vehicle {
  getDescription() {
    return `Car: ${this.make} ${this.model}, 4 wheels`;
  }
  
  getFuelEfficiency() {
    return "Average 25 MPG";
  }
}

class Motorcycle extends Vehicle {
  getDescription() {
    return `Motorcycle: ${this.make} ${this.model}, 2 wheels`;
  }
  
  getFuelEfficiency() {
    return "Average 50 MPG";
  }
}

// Demostración de polimorfismo
function describeVehicle(vehicle) {
  // El mismo método se comporta diferente según el tipo de objeto
  console.log(vehicle.getDescription());
  console.log(`Fuel efficiency: ${vehicle.getFuelEfficiency()}`);
}

const car = new Car("Toyota", "Corolla");
const motorcycle = new Motorcycle("Honda", "CBR");

describeVehicle(car);
// Car: Toyota Corolla, 4 wheels
// Fuel efficiency: Average 25 MPG

describeVehicle(motorcycle);
// Motorcycle: Honda CBR, 2 wheels
// Fuel efficiency: Average 50 MPG

En este ejemplo, la función describeVehicle() trabaja con cualquier objeto que herede de Vehicle, demostrando el principio de sustitución de Liskov: los objetos de una clase derivada deben poder sustituir a los objetos de la clase base sin afectar la funcionalidad del programa.

Verificación de tipos en jerarquías de herencia

Para trabajar eficazmente con jerarquías de clases, es útil poder verificar el tipo de un objeto:

const vehicle = new Vehicle("Generic", "Vehicle");
const car = new Car("Honda", "Civic", 2022, 4);
const electricCar = new ElectricCar("Tesla", "Model S", 2023, 4, 100);

// Verificación de instancias
console.log(car instanceof Car);          // true
console.log(car instanceof Vehicle);      // true
console.log(car instanceof ElectricCar);  // false

console.log(electricCar instanceof ElectricCar); // true
console.log(electricCar instanceof Car);         // true
console.log(electricCar instanceof Vehicle);     // true

// Verificar la clase directa
console.log(car.constructor === Car);           // true
console.log(electricCar.constructor === ElectricCar); // true

Herencia multinivel

JavaScript permite crear jerarquías de herencia multinivel, donde una clase hereda de otra que a su vez hereda de una tercera:

class SportsCar extends Car {
  constructor(make, model, year, doors, topSpeed) {
    super(make, model, year, doors);
    this.topSpeed = topSpeed;
    this.type = "sports";
  }
  
  race() {
    return `Racing at ${this.topSpeed} mph!`;
  }
}

const porsche = new SportsCar("Porsche", "911", 2023, 2, 190);
console.log(porsche.getInfo());  // "2023 Porsche 911"
console.log(porsche.start());    // "The Porsche 911 is now running"
console.log(porsche.honk());     // "Beep beep!"
console.log(porsche.race());     // "Racing at 190 mph!"

En este ejemplo, SportsCar hereda de Car, que a su vez hereda de Vehicle, formando una cadena de herencia de tres niveles.

Propiedades y métodos estáticos en la herencia

Las propiedades y métodos estáticos también se heredan:

class Vehicle {
  constructor(make, model) {
    this.make = make;
    this.model = model;
  }
  
  static getVehicleTypes() {
    return ["Car", "Truck", "Motorcycle", "Boat"];
  }
  
  static isVehicle(obj) {
    return obj instanceof Vehicle;
  }
}

class Car extends Vehicle {
  static getCarTypes() {
    return ["Sedan", "SUV", "Hatchback", "Convertible"];
  }
}

console.log(Vehicle.getVehicleTypes()); // ["Car", "Truck", "Motorcycle", "Boat"]
console.log(Car.getVehicleTypes());     // ["Car", "Truck", "Motorcycle", "Boat"]
console.log(Car.getCarTypes());         // ["Sedan", "SUV", "Hatchback", "Convertible"]

Limitaciones de la herencia en JavaScript

Aunque la sintaxis de clases facilita la implementación de herencia, JavaScript mantiene algunas limitaciones:

  • No hay herencia múltiple: Una clase solo puede extender de una única clase padre.
  • No hay interfaces: JavaScript no tiene un sistema de interfaces como Java o TypeScript.
  • No hay modificadores de acceso nativos: Aunque existen campos privados con #, no hay un sistema completo de visibilidad como protected o package-private.
// Esto NO es posible en JavaScript:
// class HybridVehicle extends Car, Boat { ... }

// Alternativa: usar composición
class HybridVehicle {
  constructor(make, model, year) {
    this.car = new Car(make, model, year, 4);
    this.boat = new Boat(make, model, year);
  }
  
  driveOnLand() {
    return this.car.start();
  }
  
  navigateWater() {
    return this.boat.sail();
  }
}

La implementación práctica de jerarquías de herencia en JavaScript moderno nos permite crear estructuras de código organizadas y reutilizables. Aunque el sistema tiene sus limitaciones, la combinación de la sintaxis de clases con el poderoso sistema de prototipos subyacente proporciona herramientas suficientes para modelar relaciones complejas entre objetos de manera efectiva.

Patrones avanzados y mejores prácticas: Composición vs. herencia, mixins y herencia múltiple simulada

Aunque la herencia es una herramienta fundamental en la programación orientada a objetos, existen patrones más avanzados que pueden ofrecer mayor flexibilidad y mantenibilidad en aplicaciones complejas. En esta sección exploraremos alternativas y complementos a la herencia tradicional en JavaScript.

Composición vs. Herencia

La composición es un patrón de diseño que favorece la construcción de objetos complejos combinando objetos más simples, en lugar de heredar funcionalidades. Este enfoque sigue el principio "favorecer la composición sobre la herencia" y ofrece varias ventajas:

// Enfoque de herencia
class Animal {
  constructor(name) {
    this.name = name;
  }
  
  eat() {
    return `${this.name} is eating`;
  }
  
  sleep() {
    return `${this.name} is sleeping`;
  }
}

class Bird extends Animal {
  fly() {
    return `${this.name} is flying`;
  }
}

// Enfoque de composición
class Eater {
  constructor(name) {
    this.name = name;
  }
  
  eat() {
    return `${this.name} is eating`;
  }
}

class Sleeper {
  constructor(name) {
    this.name = name;
  }
  
  sleep() {
    return `${this.name} is sleeping`;
  }
}

class Flyer {
  constructor(name) {
    this.name = name;
  }
  
  fly() {
    return `${this.name} is flying`;
  }
}

// Componiendo un pájaro
class BirdWithComposition {
  constructor(name) {
    this.name = name;
    this.eater = new Eater(name);
    this.sleeper = new Sleeper(name);
    this.flyer = new Flyer(name);
  }
  
  eat() {
    return this.eater.eat();
  }
  
  sleep() {
    return this.sleeper.sleep();
  }
  
  fly() {
    return this.flyer.fly();
  }
}

Ventajas de la composición:

  • Flexibilidad: Permite cambiar comportamientos en tiempo de ejecución
  • Evita el problema del diamante: No hay ambigüedad cuando dos clases padre tienen métodos con el mismo nombre
  • Código más modular: Cada comportamiento está encapsulado en su propia clase
  • Evita jerarquías profundas: Las jerarquías de herencia profundas pueden volverse difíciles de entender y mantener

Implementando Mixins

Los mixins son una forma de implementar funcionalidad reutilizable sin usar herencia. En JavaScript, podemos implementarlos de varias maneras:

1. Mixins con Object.assign()

// Definiendo mixins como objetos con métodos
const swimMixin = {
  swim() {
    return `${this.name} is swimming`;
  }
};

const flyMixin = {
  fly() {
    return `${this.name} is flying`;
  }
};

// Clase base
class Animal {
  constructor(name) {
    this.name = name;
  }
}

// Aplicando mixins a una clase
class Duck extends Animal {}
Object.assign(Duck.prototype, swimMixin, flyMixin);

// Uso
const donald = new Duck("Donald");
console.log(donald.swim()); // "Donald is swimming"
console.log(donald.fly());  // "Donald is flying"

2. Mixins como funciones de orden superior

// Definiendo mixins como funciones
function Swimming(Base) {
  return class extends Base {
    swim() {
      return `${this.name} is swimming`;
    }
  };
}

function Flying(Base) {
  return class extends Base {
    fly() {
      return `${this.name} is flying`;
    }
  };
}

// Clase base
class Animal {
  constructor(name) {
    this.name = name;
  }
}

// Componiendo múltiples mixins
class Duck extends Flying(Swimming(Animal)) {}

const daffy = new Duck("Daffy");
console.log(daffy.swim()); // "Daffy is swimming"
console.log(daffy.fly());  // "Daffy is flying"

Este enfoque utiliza herencia de clases para implementar mixins, pero de una manera más flexible que permite combinar múltiples comportamientos.

Simulando herencia múltiple

JavaScript no soporta herencia múltiple nativa, pero podemos simularla usando mixins o composición:

// Clases base
class Terrestrial {
  constructor(name) {
    this.name = name;
  }
  
  walk() {
    return `${this.name} is walking on land`;
  }
}

class Aquatic {
  constructor(name) {
    this.name = name;
  }
  
  swim() {
    return `${this.name} is swimming in water`;
  }
}

// Simulando herencia múltiple con mixins
function AquaticMixin(Base) {
  return class extends Base {
    swim() {
      return `${this.name} is swimming in water`;
    }
  };
}

class Amphibian extends AquaticMixin(Terrestrial) {
  constructor(name) {
    super(name);
  }
  
  hibernate() {
    return `${this.name} is hibernating`;
  }
}

const frog = new Amphibian("Kermit");
console.log(frog.walk()); // "Kermit is walking on land"
console.log(frog.swim()); // "Kermit is swimming in water"

Traits y decoradores

Los traits son similares a los mixins pero con algunas diferencias conceptuales. En JavaScript, podemos implementarlos de manera similar:

// Implementando traits
const HasAge = {
  getAge() {
    return this.age;
  },
  
  setAge(age) {
    this.age = age;
  }
};

const HasName = {
  getName() {
    return this.name;
  },
  
  setName(name) {
    this.name = name;
  }
};

// Aplicando traits a una clase
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

Object.assign(Person.prototype, HasName, HasAge);

const john = new Person("John", 30);
console.log(john.getName()); // "John"
console.log(john.getAge());  // 30

Decoradores de métodos

Los decoradores son una característica experimental en JavaScript que permite modificar clases y métodos. Aunque aún no están completamente estandarizados, podemos implementar un patrón decorador manualmente:

// Función decoradora
function log(target, name, descriptor) {
  const original = descriptor.value;
  
  descriptor.value = function(...args) {
    console.log(`Calling ${name} with arguments: ${args}`);
    const result = original.apply(this, args);
    console.log(`Method ${name} returned: ${result}`);
    return result;
  };
  
  return descriptor;
}

// Aplicando el decorador manualmente
class Calculator {
  add(a, b) {
    return a + b;
  }
}

// Decorando el método add
const descriptor = Object.getOwnPropertyDescriptor(Calculator.prototype, 'add');
const decoratedDescriptor = log(Calculator.prototype, 'add', descriptor);
Object.defineProperty(Calculator.prototype, 'add', decoratedDescriptor);

// Uso
const calc = new Calculator();
calc.add(2, 3);
// Salida:
// Calling add with arguments: 2,3
// Method add returned: 5

Patrones de delegación

La delegación es un patrón donde un objeto delega operaciones a otro objeto. Es una forma de composición que aprovecha el sistema de prototipos de JavaScript:

const taskActions = {
  complete() {
    this.completed = true;
    return `Task "${this.name}" has been completed`;
  },
  
  assign(person) {
    this.assignee = person;
    return `Task "${this.name}" assigned to ${person}`;
  }
};

function createTask(name, description) {
  return Object.create(taskActions, {
    name: { value: name, writable: true },
    description: { value: description, writable: true },
    completed: { value: false, writable: true },
    assignee: { value: null, writable: true },
    createdAt: { value: new Date() }
  });
}

const task = createTask("Learn JS", "Study advanced JS patterns");
console.log(task.assign("Alice")); // "Task "Learn JS" assigned to Alice"
console.log(task.complete());      // "Task "Learn JS" has been completed"

Mejores prácticas y recomendaciones

Al decidir entre herencia, composición y otros patrones, considera estas recomendaciones:

  • Usa herencia para relaciones "es un": Un gato es un animal
  • Usa composición para relaciones "tiene un": Un coche tiene un motor
  • Mantén las jerarquías de herencia poco profundas: Evita más de 2-3 niveles
  • Prefiere interfaces pequeñas: Clases con muchos métodos son difíciles de mantener
  • Considera la composición primero: Evalúa si la composición resuelve tu problema antes de usar herencia
  • Combina enfoques cuando sea apropiado: Los mejores diseños a menudo utilizan tanto herencia como composición
// Mal diseño: herencia profunda y rígida
class Vehicle {}
class LandVehicle extends Vehicle {}
class Car extends LandVehicle {}
class SportsCar extends Car {}
class Ferrari extends SportsCar {}

// Mejor diseño: composición con herencia limitada
class Vehicle {
  constructor(engine, chassis) {
    this.engine = engine;
    this.chassis = chassis;
  }
}

class Engine {
  start() { /* ... */ }
  stop() { /* ... */ }
}

class SportEngine extends Engine {
  turboBoost() { /* ... */ }
}

// Componiendo un coche deportivo
const ferrari = new Vehicle(
  new SportEngine(),
  new Chassis("carbon fiber")
);

Los patrones avanzados de herencia y composición en JavaScript ofrecen soluciones flexibles para diseñar sistemas complejos. Al comprender las ventajas y desventajas de cada enfoque, puedes seleccionar el patrón más adecuado para cada situación, creando código más mantenible, extensible y robusto.

CONSTRUYE TU CARRERA EN IA Y PROGRAMACIÓN SOFTWARE

Accede a +1000 lecciones y cursos con certificado. Mejora tu portfolio con certificados de superación para tu CV.

30 % DE DESCUENTO

Plan mensual

19.00 /mes

13.30 € /mes

Precio normal mensual: 19 €
63 % DE DESCUENTO

Plan anual

10.00 /mes

7.00 € /mes

Ahorras 144 € al año
Precio normal anual: 120 €
Aprende JavaScript online

Ejercicios de esta lección Herencia

Evalúa tus conocimientos de esta lección Herencia con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Funciones flecha

JavaScript
Puzzle

Polimorfismo

JavaScript
Test

Array

JavaScript
Código

Transformación con map()

JavaScript
Test

Gestor de tareas con JavaScript

JavaScript
Proyecto

Manipulación DOM

JavaScript
Test

Funciones

JavaScript
Test

Funciones flecha

JavaScript
Código

Async / Await

JavaScript
Código

Creación y uso de variables

JavaScript
Test

Excepciones

JavaScript
Puzzle

Promises

JavaScript
Código

Funciones cierre (closure)

JavaScript
Test

Herencia

JavaScript
Puzzle

Herencia

JavaScript
Test

Estructuras de control

JavaScript
Código

Selección de elementos DOM

JavaScript
Test

Modificación de elementos DOM

JavaScript
Test

Filtrado con filter() y find()

JavaScript
Test

Funciones cierre (closure)

JavaScript
Puzzle

Funciones

JavaScript
Puzzle

Mapas con Map

JavaScript
Test

Reducción con reduce()

JavaScript
Test

Callbacks

JavaScript
Puzzle

Manipulación DOM

JavaScript
Puzzle

Promises

JavaScript
Test

Async / Await

JavaScript
Test

Eventos del DOM

JavaScript
Puzzle

Async / Await

JavaScript
Puzzle

Promises

JavaScript
Puzzle

Filtrado con filter() y find()

JavaScript
Código

Callbacks

JavaScript
Test

Creación de clases y objetos Restaurante

JavaScript
Código

Reducción con reduce()

JavaScript
Código

Filtrado con filter() y find()

JavaScript
Puzzle

Reducción con reduce()

JavaScript
Puzzle

Conjuntos con Set

JavaScript
Puzzle

Herencia de clases

JavaScript
Código

Eventos del DOM

JavaScript
Test

Clases y objetos

JavaScript
Puzzle

Modificación de elementos DOM

JavaScript
Puzzle

Mapas con Map

JavaScript
Puzzle

Introducción a JavaScript

JavaScript
Test

Funciones

JavaScript
Código

Tipos de datos

JavaScript
Test

Clases y objetos

JavaScript
Test

Array

JavaScript
Test

Conjuntos con Set

JavaScript
Test

Array

JavaScript
Puzzle

Encapsulación

JavaScript
Puzzle

Clases y objetos

JavaScript
Código

Uso de operadores

JavaScript
Puzzle

Uso de operadores

JavaScript
Test

Estructuras de control

JavaScript
Test

Excepciones

JavaScript
Test

Transformación con map()

JavaScript
Puzzle

Funciones flecha

JavaScript
Test

Selección de elementos DOM

JavaScript
Puzzle

Encapsulación

JavaScript
Test

Mapas con Map

JavaScript
Código

Creación y uso de variables

JavaScript
Puzzle

Polimorfismo

JavaScript
Puzzle

Tipos de datos

JavaScript
Puzzle

Estructuras de control

JavaScript
Puzzle

Todas las lecciones de JavaScript

Accede a todas las lecciones de JavaScript y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a JavaScript y certifícate

Certificados de superación de JavaScript

Supera todos los ejercicios de programación del curso de JavaScript y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.

En esta lección

Objetivos de aprendizaje de esta lección

  1. Comprender el concepto de herencia en JavaScript y cómo se basa en prototipos.
  2. Conocer el uso de la propiedad [[Prototype]] o __proto__ para establecer la cadena de prototipos.
  3. Aprender cómo crear objetos y funciones constructoras para establecer relaciones de herencia.
  4. Entender cómo la herencia de atributos y métodos funciona a través de la cadena de prototipos.
  5. Aprender a utilizar la función Object.create() para crear objetos con un prototipo específico.
  6. Conocer el uso de las palabras clave extends y super en las clases de ES6 para simplificar la herencia.
  7. Entender cómo agregar o sobrescribir métodos en la cadena de prototipos para personalizar el comportamiento de objetos heredados.
  8. Saber cómo la herencia permite reutilizar código y compartir comportamientos y estructuras entre objetos en JavaScript.