JavaScript
Tutorial JavaScript: Polimorfismo
JavaScript polimorfismo: técnicas y ejemplos. Domina las técnicas de polimorfismo en JavaScript con ejemplos prácticos y detallados.
Aprende JavaScript y certifícateFundamentos del polimorfismo: Múltiples formas, misma interfaz en la programación orientada a objetos
El polimorfismo es uno de los pilares fundamentales de la programación orientada a objetos, permitiendo que objetos de diferentes clases respondan al mismo mensaje o método de manera distinta. La palabra proviene del griego "poly" (muchos) y "morphos" (formas), literalmente "muchas formas".
En su esencia, el polimorfismo permite tratar objetos de diferentes tipos a través de una interfaz común. Esto facilita escribir código más flexible, modular y reutilizable, ya que podemos trabajar con diferentes implementaciones sin necesidad de conocer sus detalles específicos.
Concepto básico del polimorfismo
Imagina que tienes diferentes tipos de vehículos: coches, motocicletas y bicicletas. Aunque cada uno funciona de manera distinta internamente, todos comparten la capacidad de "moverse". El polimorfismo nos permite invocar este comportamiento común sin preocuparnos por los detalles específicos de implementación de cada vehículo.
class Vehicle {
move() {
throw new Error("Method 'move()' must be implemented");
}
}
class Car extends Vehicle {
move() {
return "Car is driving on the road";
}
}
class Motorcycle extends Vehicle {
move() {
return "Motorcycle is riding on the street";
}
}
La belleza del polimorfismo radica en poder tratar todos estos objetos de manera uniforme:
function startJourney(vehicle) {
// No necesitamos saber qué tipo de vehículo es
console.log(vehicle.move());
}
const myCar = new Car();
const myMotorcycle = new Motorcycle();
startJourney(myCar); // "Car is driving on the road"
startJourney(myMotorcycle); // "Motorcycle is riding on the street"
Tipos de polimorfismo en JavaScript
En JavaScript, podemos identificar principalmente dos tipos de polimorfismo:
- 1. Polimorfismo de sobrescritura (Override): Ocurre cuando una clase hija redefine un método heredado de su clase padre.
class Shape {
calculateArea() {
return 0;
}
}
class Circle extends Shape {
constructor(radius) {
super();
this.radius = radius;
}
calculateArea() {
return Math.PI * this.radius * this.radius;
}
}
- 2. Polimorfismo de interfaz: Se basa en que diferentes objetos implementen los mismos métodos, aunque no estén relacionados por herencia.
// Dos clases sin relación de herencia
class Duck {
sound() {
return "Quack!";
}
}
class Dog {
sound() {
return "Woof!";
}
}
// Función que trabaja con cualquier objeto que tenga el método 'sound'
function makeSound(animal) {
console.log(animal.sound());
}
Beneficios del polimorfismo
El polimorfismo ofrece varias ventajas clave para el diseño de software:
- Flexibilidad: Permite que el código funcione con diferentes tipos de objetos sin modificaciones.
- Extensibilidad: Facilita añadir nuevos tipos sin alterar el código existente.
- Mantenibilidad: Reduce la duplicación de código y mejora la organización.
- Abstracción: Permite centrarse en "qué hace" en lugar de "cómo lo hace".
Polimorfismo y principio de sustitución de Liskov
El polimorfismo está estrechamente relacionado con el Principio de Sustitución de Liskov, que establece que los objetos de una clase derivada deben poder sustituir a los objetos de la clase base sin afectar la corrección del programa.
class Bird {
fly() {
return "Flying high";
}
}
class Sparrow extends Bird {
// Comportamiento compatible con la clase base
fly() {
return "Sparrow flying at medium altitude";
}
}
// Uso polimórfico
function letBirdFly(bird) {
return bird.fly();
}
const genericBird = new Bird();
const sparrow = new Sparrow();
console.log(letBirdFly(genericBird)); // "Flying high"
console.log(letBirdFly(sparrow)); // "Sparrow flying at medium altitude"
Polimorfismo vs sobrecarga de métodos
Es importante distinguir entre polimorfismo y sobrecarga de métodos:
- El polimorfismo permite que diferentes clases implementen el mismo método de diferentes maneras.
- La sobrecarga permite definir múltiples versiones del mismo método con diferentes parámetros.
JavaScript no soporta nativamente la sobrecarga de métodos como otros lenguajes, pero podemos simularla mediante la verificación de argumentos:
class Calculator {
add(...args) {
// Polimorfismo basado en argumentos
if (args.length === 0) return 0;
if (args.length === 1) return args[0];
return args.reduce((sum, val) => sum + val, 0);
}
}
const calc = new Calculator();
console.log(calc.add()); // 0
console.log(calc.add(5)); // 5
console.log(calc.add(2, 3, 4)); // 9
El polimorfismo es un concepto fundamental que permite crear sistemas más modulares y adaptables. En JavaScript, gracias a su naturaleza dinámica, el polimorfismo se implementa de manera más flexible que en lenguajes estáticamente tipados, permitiendo crear interfaces comunes entre objetos sin necesidad de estructuras formales.
Implementación práctica de polimorfismo en JavaScript: Herencia, duck typing y composición
El polimorfismo en JavaScript puede implementarse a través de diferentes técnicas que aprovechan la flexibilidad del lenguaje. Vamos a explorar las tres aproximaciones principales: herencia, duck typing y composición, cada una con sus propias ventajas y casos de uso.
Polimorfismo mediante herencia
La herencia es la forma más tradicional de implementar polimorfismo en JavaScript. Con la introducción de las clases en ES6, esta aproximación se volvió más intuitiva:
class PaymentMethod {
processPayment(amount) {
throw new Error("This method must be implemented");
}
getDescription() {
return "Generic payment method";
}
}
class CreditCard extends PaymentMethod {
constructor(cardNumber, expiryDate) {
super();
this.cardNumber = cardNumber;
this.expiryDate = expiryDate;
}
processPayment(amount) {
return `Processing $${amount} via Credit Card ending in ${this.cardNumber.slice(-4)}`;
}
getDescription() {
return `Credit Card ending in ${this.cardNumber.slice(-4)}`;
}
}
class PayPal extends PaymentMethod {
constructor(email) {
super();
this.email = email;
}
processPayment(amount) {
return `Processing $${amount} via PayPal account: ${this.email}`;
}
getDescription() {
return `PayPal (${this.email})`;
}
}
Podemos utilizar estas clases de forma polimórfica:
function checkout(cart, paymentMethod) {
const total = cart.calculateTotal();
console.log(`Total: $${total}`);
console.log(`Payment method: ${paymentMethod.getDescription()}`);
console.log(paymentMethod.processPayment(total));
}
const myCart = { calculateTotal: () => 125.40 };
const creditCard = new CreditCard("4111111111111111", "12/25");
const paypal = new PayPal("user@example.com");
checkout(myCart, creditCard);
checkout(myCart, paypal);
Polimorfismo mediante duck typing
JavaScript, al ser un lenguaje de tipado dinámico, permite implementar polimorfismo a través del duck typing: "si camina como un pato y grazna como un pato, entonces probablemente sea un pato". Esta aproximación se centra en el comportamiento de los objetos más que en su tipo o jerarquía:
// No hay herencia, solo objetos con métodos similares
const audioPlayer = {
play(track) {
return `Playing audio track: ${track}`;
},
stop() {
return "Audio playback stopped";
}
};
const videoPlayer = {
play(video) {
return `Playing video: ${video}`;
},
stop() {
return "Video playback stopped";
}
};
// Función que trabaja con cualquier "reproductor"
function useMediaPlayer(player, mediaName) {
console.log(player.play(mediaName));
// Más operaciones...
console.log(player.stop());
}
useMediaPlayer(audioPlayer, "song.mp3");
useMediaPlayer(videoPlayer, "movie.mp4");
El duck typing es extremadamente flexible y no requiere relaciones de herencia, pero depende de convenciones implícitas que deben ser documentadas adecuadamente.
Polimorfismo mediante composición
La composición ofrece una alternativa a la herencia, siguiendo el principio de "componer en lugar de heredar". Esta técnica consiste en construir objetos complejos combinando objetos más simples:
// Comportamientos como objetos independientes
const swimmer = {
swim() {
return "Swimming";
}
};
const flyer = {
fly() {
return "Flying";
}
};
const walker = {
walk() {
return "Walking";
}
};
// Creamos objetos compuestos
function createDuck(name) {
return {
name,
...swimmer,
...flyer,
...walker,
makeSound() {
return "Quack!";
}
};
}
function createPenguin(name) {
return {
name,
...swimmer,
...walker,
makeSound() {
return "Honk!";
}
};
}
// Uso polimórfico basado en capacidades
function letSwim(creature) {
if ('swim' in creature) {
return `${creature.name} is ${creature.swim()}`;
}
return `${creature.name} cannot swim`;
}
const donald = createDuck("Donald");
const pingu = createPenguin("Pingu");
console.log(letSwim(donald)); // "Donald is Swimming"
console.log(letSwim(pingu)); // "Pingu is Swimming"
La composición permite crear sistemas más flexibles donde los comportamientos pueden combinarse de múltiples formas sin las limitaciones de la herencia simple.
Mixins: Extendiendo el polimorfismo por composición
Los mixins son una técnica avanzada de composición que permite añadir funcionalidades a clases existentes:
// Definimos mixins como funciones que extienden una clase
const TimestampMixin = (Base) => class extends Base {
getCreatedAt() {
return this.createdAt || (this.createdAt = new Date());
}
};
const ValidationMixin = (Base) => class extends Base {
validate() {
return this.isValid ? "Valid" : "Invalid";
}
};
// Clase base
class User {
constructor(name) {
this.name = name;
this.isValid = true;
}
}
// Aplicamos mixins
class EnhancedUser extends ValidationMixin(TimestampMixin(User)) {
getDetails() {
return `${this.name}, created: ${this.getCreatedAt()}, status: ${this.validate()}`;
}
}
const user = new EnhancedUser("Alice");
console.log(user.getDetails());
Comparación de enfoques
Cada enfoque tiene sus ventajas y desventajas:
Herencia:
Ventajas: Estructura clara, fácil de entender, bien soportada por el lenguaje.
Desventajas: Jerarquías rígidas, problemas con herencia múltiple.
Duck typing:
Ventajas: Extremadamente flexible, sin restricciones de jerarquía.
Desventajas: Sin verificación en tiempo de compilación, puede llevar a errores sutiles.
Composición:
Ventajas: Modular, flexible, evita problemas de la herencia múltiple.
Desventajas: Puede resultar en más código, requiere diseño cuidadoso.
Aplicación práctica: Sistema de notificaciones
Veamos un ejemplo práctico de un sistema de notificaciones que utiliza polimorfismo:
// Interfaz común (implícita en JavaScript)
class NotificationChannel {
send(message) {
throw new Error("Method not implemented");
}
}
class EmailNotification extends NotificationChannel {
constructor(email) {
super();
this.email = email;
}
send(message) {
return `Email to ${this.email}: ${message}`;
}
}
class SMSNotification extends NotificationChannel {
constructor(phoneNumber) {
super();
this.phoneNumber = phoneNumber;
}
send(message) {
return `SMS to ${this.phoneNumber}: ${message}`;
}
}
class PushNotification extends NotificationChannel {
constructor(deviceId) {
super();
this.deviceId = deviceId;
}
send(message) {
return `Push to device ${this.deviceId}: ${message}`;
}
}
// Sistema de notificación que usa polimorfismo
function notifyUser(channels, message) {
return channels.map(channel => channel.send(message));
}
// Uso
const userChannels = [
new EmailNotification("user@example.com"),
new SMSNotification("+1234567890"),
new PushNotification("device-xyz-123")
];
const notifications = notifyUser(userChannels, "Your order has shipped!");
notifications.forEach(n => console.log(n));
Este ejemplo demuestra cómo el polimorfismo nos permite tratar diferentes canales de notificación de manera uniforme, facilitando la extensión del sistema con nuevos canales sin modificar la lógica principal.
Patrones y mejores prácticas: Diseño de sistemas polimórficos mantenibles y extensibles
Diseñar sistemas polimórficos que sean mantenibles y extensibles requiere más que simplemente implementar interfaces comunes. Necesitamos aplicar patrones de diseño y seguir prácticas que garanticen que nuestro código permanezca flexible a lo largo del tiempo, incluso cuando los requisitos cambien o el sistema crezca.
Principios SOLID en sistemas polimórficos
Los principios SOLID proporcionan una base sólida para diseñar sistemas polimórficos efectivos:
- Principio de responsabilidad única (SRP): Cada clase debe tener una única razón para cambiar.
// En lugar de una clase que haga todo
class UserManager {
constructor(database) {
this.database = database;
}
// Separación de responsabilidades
authenticate(credentials) {
// Lógica de autenticación
return this.database.verifyUser(credentials);
}
// Delegamos la notificación a otra clase
notifyUser(user, message) {
const notifier = new UserNotifier();
return notifier.send(user, message);
}
}
- Principio abierto/cerrado (OCP): Las entidades deben estar abiertas para extensión pero cerradas para modificación.
// Sistema de filtros extensible
class ProductFilter {
filter(products, specification) {
return products.filter(product => specification.isSatisfied(product));
}
}
// Especificaciones que podemos combinar y extender
class PriceSpecification {
constructor(maxPrice) {
this.maxPrice = maxPrice;
}
isSatisfied(product) {
return product.price <= this.maxPrice;
}
}
class CategorySpecification {
constructor(category) {
this.category = category;
}
isSatisfied(product) {
return product.category === this.category;
}
}
- Principio de inversión de dependencias (DIP): Depender de abstracciones, no de implementaciones concretas.
// Dependemos de una interfaz, no de implementaciones específicas
class OrderProcessor {
constructor(paymentGateway) {
this.paymentGateway = paymentGateway;
}
processOrder(order) {
// Trabajamos con cualquier gateway que implemente process()
return this.paymentGateway.process(order.total);
}
}
// Diferentes implementaciones de la misma interfaz
class StripeGateway {
process(amount) {
return `Processed $${amount} through Stripe`;
}
}
class PayPalGateway {
process(amount) {
return `Processed $${amount} through PayPal`;
}
}
Patrones de diseño para sistemas polimórficos
Ciertos patrones de diseño son particularmente útiles para crear sistemas polimórficos robustos:
- Patrón Strategy: Permite definir una familia de algoritmos intercambiables.
// Contexto que usa diferentes estrategias
class ShippingCalculator {
constructor(strategy) {
this.strategy = strategy;
}
setStrategy(strategy) {
this.strategy = strategy;
}
calculate(package) {
return this.strategy.calculate(package);
}
}
// Estrategias intercambiables
class StandardShipping {
calculate(package) {
return package.weight * 1.5;
}
}
class ExpressShipping {
calculate(package) {
return package.weight * 2.5;
}
}
// Uso
const calculator = new ShippingCalculator(new StandardShipping());
const cost = calculator.calculate({weight: 5});
- Patrón Factory: Centraliza la creación de objetos polimórficos.
// Factory que crea diferentes tipos de loggers
class LoggerFactory {
static createLogger(type, config) {
switch(type) {
case 'console':
return new ConsoleLogger(config);
case 'file':
return new FileLogger(config);
case 'remote':
return new RemoteLogger(config);
default:
throw new Error(`Logger type ${type} not supported`);
}
}
}
// Uso
const logger = LoggerFactory.createLogger('console', {level: 'info'});
logger.log('System started');
- Patrón Composite: Permite tratar objetos individuales y composiciones de objetos de manera uniforme.
// Componente base
class UIComponent {
render() {
throw new Error('Must implement render method');
}
}
// Componente hoja
class Button extends UIComponent {
constructor(text) {
super();
this.text = text;
}
render() {
return `<button>${this.text}</button>`;
}
}
// Componente compuesto
class Panel extends UIComponent {
constructor() {
super();
this.children = [];
}
add(component) {
this.children.push(component);
return this;
}
render() {
return `<div>${this.children.map(child => child.render()).join('')}</div>`;
}
}
// Uso polimórfico
const loginPanel = new Panel()
.add(new Button('Login'))
.add(new Button('Cancel'));
document.body.innerHTML = loginPanel.render();
Técnicas de extensibilidad
Para mantener los sistemas extensibles a largo plazo:
- Plugins y hooks: Proporciona puntos de extensión explícitos.
class Application {
constructor() {
this.plugins = [];
this.hooks = {
beforeInit: [],
afterInit: [],
beforeShutdown: []
};
}
registerPlugin(plugin) {
this.plugins.push(plugin);
// El plugin puede registrar sus propios hooks
if (plugin.registerHooks) {
plugin.registerHooks(this);
}
}
on(hookName, callback) {
if (this.hooks[hookName]) {
this.hooks[hookName].push(callback);
}
}
triggerHook(hookName, data) {
if (this.hooks[hookName]) {
for (const callback of this.hooks[hookName]) {
callback(data);
}
}
}
init() {
this.triggerHook('beforeInit');
// Inicialización
this.triggerHook('afterInit');
}
}
- Middleware: Permite insertar comportamiento en una cadena de procesamiento.
class RequestProcessor {
constructor() {
this.middlewares = [];
}
use(middleware) {
this.middlewares.push(middleware);
return this;
}
process(request) {
// Creamos una cadena de ejecución
const chain = this.middlewares.reduceRight(
(next, middleware) => {
return (req) => middleware(req, next);
},
(req) => req // Middleware final que devuelve la solicitud
);
return chain(request);
}
}
// Middlewares
const authenticate = (req, next) => {
console.log('Authenticating...');
req.authenticated = true;
return next(req);
};
const validate = (req, next) => {
console.log('Validating...');
req.validated = true;
return next(req);
};
// Uso
const processor = new RequestProcessor()
.use(authenticate)
.use(validate);
const result = processor.process({url: '/api/data'});
Evitando trampas comunes
Hay varios anti-patrones que debemos evitar al diseñar sistemas polimórficos:
- Verificación de tipos excesiva: Confía en el polimorfismo en lugar de verificar tipos.
// Evita esto
function processShape(shape) {
if (shape instanceof Circle) {
// Lógica específica para círculos
} else if (shape instanceof Rectangle) {
// Lógica específica para rectángulos
}
}
// Prefiere esto
function processShape(shape) {
// Confía en que shape implementa calculateArea()
const area = shape.calculateArea();
// Procesamiento genérico
}
- Jerarquías de herencia profundas: Limita la profundidad de tus jerarquías.
// Evita jerarquías profundas como:
// Entity -> Character -> Player -> SpecialPlayer -> ...
// Prefiere composición:
class Player {
constructor(name) {
this.name = name;
this.abilities = [];
}
addAbility(ability) {
this.abilities.push(ability);
}
useAbility(abilityName, target) {
const ability = this.abilities.find(a => a.name === abilityName);
if (ability) {
return ability.execute(this, target);
}
return null;
}
}
- Acoplamiento excesivo: Minimiza las dependencias entre clases.
// Evita que las clases conozcan demasiado sobre otras
class OrderProcessor {
constructor(paymentService, inventoryService, shippingService) {
this.paymentService = paymentService;
this.inventoryService = inventoryService;
this.shippingService = shippingService;
}
// Cada servicio tiene una interfaz clara y limitada
processOrder(order) {
if (this.paymentService.processPayment(order.payment)) {
this.inventoryService.updateStock(order.items);
return this.shippingService.createShipment(order);
}
return false;
}
}
Pruebas de sistemas polimórficos
Las pruebas son cruciales para mantener sistemas polimórficos robustos:
- Pruebas de contrato: Verifica que todas las implementaciones cumplan con el contrato esperado.
// Función de prueba genérica para cualquier storage
function testStorageImplementation(storage) {
// Prueba el contrato básico
storage.set('key', 'value');
const value = storage.get('key');
assert(value === 'value', 'Should retrieve stored value');
storage.delete('key');
const deleted = storage.get('key');
assert(deleted === undefined, 'Should return undefined for deleted keys');
}
// Prueba múltiples implementaciones
testStorageImplementation(new MemoryStorage());
testStorageImplementation(new LocalStorage());
testStorageImplementation(new RedisStorage());
- Mocks y stubs: Facilitan probar componentes que dependen de interfaces polimórficas.
// Prueba con un mock que implementa la interfaz
function testNotificationSystem(notifier) {
// Mock de un canal de notificación
const mockChannel = {
send: jest.fn().mockReturnValue(true)
};
notifier.addChannel(mockChannel);
notifier.notify('Test message');
// Verificamos que se llamó al método send
expect(mockChannel.send).toHaveBeenCalledWith('Test message');
}
Evolución de sistemas polimórficos
Para que los sistemas polimórficos evolucionen correctamente:
- Versionado de interfaces: Gestiona cambios en las interfaces con cuidado.
// Versión 1 de la API
class APIClientV1 {
fetchUsers() {
return fetch('/api/v1/users').then(r => r.json());
}
}
// Versión 2 con parámetros adicionales
class APIClientV2 {
fetchUsers(options = {}) {
const params = new URLSearchParams(options);
return fetch(`/api/v2/users?${params}`).then(r => r.json());
}
}
// Adaptador para mantener compatibilidad
class APIClientAdapter {
constructor(client) {
this.client = client;
}
fetchUsers(options) {
// Si es v1, ignoramos options
if (this.client instanceof APIClientV1) {
return this.client.fetchUsers();
}
return this.client.fetchUsers(options);
}
}
- Refactorización gradual: Evoluciona el sistema sin romper la funcionalidad existente.
// Enfoque de strangler pattern para migrar sistemas
class LegacyPaymentProcessor {
process(payment) {
// Lógica antigua
}
}
class ModernPaymentProcessor {
processPayment(paymentData) {
// Nueva implementación mejorada
}
}
// Adaptador para migración gradual
class PaymentProcessorAdapter {
constructor(modernProcessor) {
this.modernProcessor = modernProcessor;
}
process(payment) {
// Traduce del formato antiguo al nuevo
return this.modernProcessor.processPayment({
amount: payment.amount,
method: payment.type,
currency: payment.currency || 'USD'
});
}
}
Aplicando estos patrones y prácticas, podemos crear sistemas polimórficos que no solo funcionen hoy, sino que también puedan adaptarse a los cambios futuros con un mínimo de fricción. El polimorfismo bien implementado nos permite escribir código que abraza el cambio en lugar de resistirlo.
Ejercicios de esta lección Polimorfismo
Evalúa tus conocimientos de esta lección Polimorfismo con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Funciones flecha
Polimorfismo
Array
Transformación con map()
Gestor de tareas con JavaScript
Manipulación DOM
Funciones
Funciones flecha
Async / Await
Creación y uso de variables
Excepciones
Promises
Funciones cierre (closure)
Herencia
Herencia
Estructuras de control
Selección de elementos DOM
Modificación de elementos DOM
Filtrado con filter() y find()
Funciones cierre (closure)
Funciones
Mapas con Map
Reducción con reduce()
Callbacks
Manipulación DOM
Promises
Async / Await
Eventos del DOM
Async / Await
Promises
Filtrado con filter() y find()
Callbacks
Creación de clases y objetos Restaurante
Reducción con reduce()
Filtrado con filter() y find()
Reducción con reduce()
Conjuntos con Set
Herencia de clases
Eventos del DOM
Clases y objetos
Modificación de elementos DOM
Mapas con Map
Introducción a JavaScript
Funciones
Tipos de datos
Clases y objetos
Array
Conjuntos con Set
Array
Encapsulación
Clases y objetos
Uso de operadores
Uso de operadores
Estructuras de control
Excepciones
Transformación con map()
Funciones flecha
Selección de elementos DOM
Encapsulación
Mapas con Map
Creación y uso de variables
Polimorfismo
Tipos de datos
Estructuras de control
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.
Introducción A Javascript
Introducción Y Entorno
Tipos De Datos
Sintaxis
Variables
Sintaxis
Operadores
Sintaxis
Estructuras De Control
Sintaxis
Funciones
Sintaxis
Funciones Cierre (Closure)
Sintaxis
Arrays Y Métodos
Estructuras De Datos
Conjuntos Con Set
Estructuras De Datos
Mapas Con Map
Estructuras De Datos
Funciones Flecha
Programación Funcional
Filtrado Con Filter() Y Find()
Programación Funcional
Transformación Con Map()
Programación Funcional
Reducción Con Reduce()
Programación Funcional
Clases Y Objetos
Programación Orientada A Objetos
Excepciones
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Manipulación Dom
Dom
Selección De Elementos Dom
Dom
Modificación De Elementos Dom
Dom
Eventos Del Dom
Dom
Callbacks
Programación Asíncrona
Promises
Programación Asíncrona
Async / Await
Programación Asíncrona
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
- Comprender el concepto de polimorfismo y su importancia en la programación orientada a objetos.
- Conocer cómo la herencia en JavaScript facilita la implementación del polimorfismo.
- Aprender a utilizar funciones constructoras y objetos prototipo para crear subclases con implementaciones específicas de métodos.
- Entender cómo el polimorfismo permite tratar diferentes objetos como si fueran de una misma clase, ejecutando métodos compartidos sin conocer los detalles específicos de cada objeto.
- Conocer el polimorfismo ad hoc en JavaScript, que permite trabajar con diferentes tipos de datos en una función sin crear múltiples funciones similares.
- Aprender sobre la composición de objetos y cómo esta técnica proporciona polimorfismo al agregar comportamientos únicos a un objeto sin recurrir a la herencia típica.
- Saber cómo el polimorfismo en JavaScript brinda flexibilidad y escalabilidad en el diseño y la implementación de soluciones de software.