JavaScript
Tutorial JavaScript: Encapsulación
JavaScript encapsulación: técnicas y ejemplos. Aprende técnicas de encapsulación en JavaScript con ejemplos prácticos y detallados.
Aprende JavaScript y certifícateFundamentos de la encapsulación: Ocultamiento de datos y control de acceso en JavaScript
La encapsulación es uno de los pilares fundamentales de la programación orientada a objetos que permite ocultar los detalles internos de implementación de un objeto y exponer solo aquellas partes que son necesarias para su uso. En JavaScript, este concepto ha evolucionado significativamente con el tiempo, adaptándose a las particularidades del lenguaje.
Principios básicos de la encapsulación
La encapsulación se basa en dos conceptos principales:
- Ocultamiento de datos: Restringir el acceso directo a ciertos componentes del objeto
- Control de acceso: Proporcionar interfaces controladas para interactuar con esos datos
El objetivo principal es proteger la integridad de los datos internos de un objeto, evitando modificaciones accidentales o malintencionadas desde el exterior.
Convenciones de nomenclatura
Históricamente, JavaScript ha utilizado convenciones de nomenclatura para indicar que ciertas propiedades deberían tratarse como privadas:
class BankAccount {
constructor(owner) {
this.owner = owner;
this._balance = 0; // El prefijo _ indica "privado por convención"
}
deposit(amount) {
if (amount > 0) {
this._balance += amount;
return true;
}
return false;
}
getBalance() {
return this._balance;
}
}
En este ejemplo, _balance
utiliza el prefijo _
para indicar que es una propiedad que no debería accederse directamente desde fuera de la clase. Sin embargo, esta es solo una convención y no impone ninguna restricción real:
const account = new BankAccount("Alice");
account.deposit(100);
console.log(account.getBalance()); // 100 (acceso correcto)
account._balance = 1000000; // Funciona, pero rompe la encapsulación
Closures para encapsulación
Antes de la introducción de clases en ES6, los closures eran el mecanismo principal para implementar encapsulación real en JavaScript:
function createCounter() {
let count = 0; // Variable privada
return {
increment() {
count++;
},
decrement() {
count--;
},
getValue() {
return count;
}
};
}
const counter = createCounter();
counter.increment();
console.log(counter.getValue()); // 1
console.log(counter.count); // undefined - no hay acceso directo
En este patrón, la variable count
está encapsulada dentro del closure y solo es accesible a través de los métodos proporcionados. Este enfoque ofrece una verdadera encapsulación, ya que no hay forma de acceder directamente a la variable privada.
Getters y setters
Los getters y setters proporcionan una forma más elegante de controlar el acceso a las propiedades:
class Temperature {
constructor(celsius) {
this._celsius = celsius;
}
get celsius() {
return this._celsius;
}
set celsius(value) {
if (value < -273.15) {
throw new Error("Temperature below absolute zero is not possible");
}
this._celsius = value;
}
get fahrenheit() {
return this._celsius * 9/5 + 32;
}
set fahrenheit(value) {
this.celsius = (value - 32) * 5/9;
}
}
Los getters y setters permiten:
- Validar datos antes de asignarlos
- Calcular valores derivados bajo demanda
- Mantener la consistencia entre propiedades relacionadas
- Ocultar la implementación interna
const temp = new Temperature(25);
console.log(temp.celsius); // 25
console.log(temp.fahrenheit); // 77
temp.celsius = 30;
console.log(temp.fahrenheit); // 86
// Validación en acción
try {
temp.celsius = -300; // Error: Temperature below absolute zero
} catch (e) {
console.log(e.message);
}
Métodos de objeto para control de acceso
JavaScript ofrece métodos nativos para controlar el comportamiento de las propiedades de un objeto:
class SafeBox {
constructor(content) {
this._content = content;
// Hacer que _content no sea enumerable ni configurable
Object.defineProperty(this, '_content', {
enumerable: false, // No aparece en Object.keys()
configurable: false, // No puede ser eliminado
writable: false // No puede ser modificado directamente
});
}
getContent(password) {
if (password === "secret") {
return this._content;
}
return "Access denied";
}
}
Con Object.defineProperty()
podemos:
- Controlar si una propiedad es enumerable (visible en iteraciones)
- Definir si es configurable (puede ser eliminada)
- Establecer si es writable (puede ser modificada)
- Implementar getters y setters personalizados
Símbolos para propiedades semi-privadas
Los símbolos introducidos en ES6 ofrecen otra forma de implementar propiedades semi-privadas:
const passwordSymbol = Symbol('password');
class User {
constructor(username, password) {
this.username = username;
this[passwordSymbol] = password;
}
validatePassword(input) {
return this[passwordSymbol] === input;
}
}
const user = new User("admin", "1234");
console.log(user.validatePassword("1234")); // true
console.log(user[passwordSymbol]); // Accesible si se conoce el símbolo
console.log(Object.keys(user)); // ["username"] - el símbolo no aparece
Las propiedades basadas en símbolos:
- No aparecen en iteraciones estándar como
Object.keys()
- No son accesibles directamente sin conocer el símbolo
- Proporcionan un nivel de privacidad práctica aunque no absoluta
La encapsulación en JavaScript ha evolucionado desde simples convenciones hasta mecanismos más robustos. Aunque ninguna de estas técnicas tradicionales ofrece una encapsulación perfecta, proporcionan diferentes niveles de protección y control de acceso que son útiles en distintos contextos de desarrollo.
Técnicas modernas para la implementación de encapsulación: Campos privados y patrones de módulo
JavaScript ha evolucionado significativamente en los últimos años, introduciendo características nativas que permiten implementar una encapsulación más robusta y elegante. Las técnicas modernas ofrecen soluciones que van más allá de las convenciones y patrones tradicionales, proporcionando mecanismos de encapsulación con mayor integridad y claridad sintáctica.
Campos privados con # (ECMAScript 2022)
La adición más importante para la encapsulación en JavaScript moderno es la implementación de campos privados utilizando el prefijo #
. Esta característica proporciona una verdadera privacidad a nivel de lenguaje:
class CreditCard {
#number;
#cvv;
#expirationDate;
name;
constructor(number, cvv, expirationDate, name) {
this.#number = number;
this.#cvv = cvv;
this.#expirationDate = expirationDate;
this.name = name;
}
getLastFourDigits() {
return this.#number.slice(-4);
}
isValid() {
const now = new Date();
return now < this.#expirationDate;
}
}
A diferencia de las convenciones con guion bajo, los campos privados con #
están estrictamente encapsulados:
const myCard = new CreditCard("1234567890123456", "123", new Date(2025, 0), "John Doe");
console.log(myCard.name); // "John Doe" - campo público
console.log(myCard.getLastFourDigits()); // "3456"
// Intentos de acceso directo generan errores
try {
console.log(myCard.#number); // SyntaxError
} catch (e) {
console.log("No se puede acceder a campos privados desde fuera");
}
Los campos privados con #
ofrecen varias ventajas clave:
- Son verdaderamente privados y no solo por convención
- Generan un error de sintaxis si se intenta acceder desde fuera
- No pueden ser descubiertos mediante técnicas de introspección como
Object.keys()
- Funcionan con métodos, getters y setters privados
Métodos privados y accesorios
La sintaxis de #
también se aplica a métodos y accesorios privados:
class PaymentProcessor {
#apiKey;
#endpoint = "https://payment.example.com/api";
constructor(apiKey) {
this.#apiKey = apiKey;
}
async processPayment(amount, currency) {
const data = await this.#sendRequest({
amount,
currency,
timestamp: this.#generateTimestamp()
});
return this.#parseResponse(data);
}
#generateTimestamp() {
return Date.now();
}
async #sendRequest(payload) {
const response = await fetch(this.#endpoint, {
method: "POST",
headers: {
"Authorization": `Bearer ${this.#apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});
return response.json();
}
#parseResponse(data) {
return {
success: data.status === "approved",
transactionId: data.id,
timestamp: new Date(data.processed_at)
};
}
}
Este enfoque permite crear APIs limpias donde solo los métodos públicos son visibles para los consumidores de la clase, mientras que los detalles de implementación permanecen encapsulados.
Getters y setters privados
También podemos definir getters y setters privados para operaciones internas:
class TemperatureSensor {
#celsius;
#lastUpdated;
#maxReadings = 100;
#readings = [];
constructor(initialCelsius) {
this.#celsius = initialCelsius;
this.#lastUpdated = new Date();
}
get temperature() {
return {
celsius: this.#celsius,
fahrenheit: this.#fahrenheit,
lastUpdated: this.#lastUpdated
};
}
// Getter privado
get #fahrenheit() {
return this.#celsius * 9/5 + 32;
}
updateReading(newCelsius) {
this.#celsius = newCelsius;
this.#lastUpdated = new Date();
this.#addReading(newCelsius);
return this.temperature;
}
// Método privado
#addReading(value) {
this.#readings.push({
value,
timestamp: new Date()
});
if (this.#readings.length > this.#maxReadings) {
this.#readings.shift();
}
}
}
Patrón de módulo moderno con ESM
El sistema de módulos nativo de JavaScript (ESM) proporciona otra capa de encapsulación a nivel de archivo:
// userService.js
const API_KEY = "secret_api_key"; // Variable privada al módulo
function validateEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
export function createUser(userData) {
if (!validateEmail(userData.email)) {
throw new Error("Invalid email format");
}
// Lógica que usa API_KEY internamente
return fetch("https://api.example.com/users", {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify(userData)
}).then(res => res.json());
}
export function getUser(id) {
// Implementación que usa API_KEY
}
// No exportamos validateEmail ni API_KEY
En otro archivo, solo podemos acceder a lo que se exporta explícitamente:
// app.js
import { createUser, getUser } from './userService.js';
// Podemos usar las funciones exportadas
createUser({ name: "Alice", email: "alice@example.com" });
// Pero no tenemos acceso a las partes privadas
// API_KEY y validateEmail no son accesibles aquí
Este patrón permite:
- Encapsular variables y funciones a nivel de módulo
- Exponer solo una API pública bien definida
- Mantener la seguridad de datos sensibles como claves API
- Organizar el código en unidades cohesivas
Combinando módulos y clases con campos privados
La combinación de módulos ESM y campos privados proporciona múltiples capas de encapsulación:
// database.js
const DB_CONNECTION_STRING = "mongodb://username:password@localhost:27017";
class DatabaseConnection {
#connection = null;
#retryCount = 0;
#maxRetries = 3;
async #connect() {
try {
// Lógica de conexión usando DB_CONNECTION_STRING
this.#retryCount = 0;
return true;
} catch (error) {
if (this.#retryCount < this.#maxRetries) {
this.#retryCount++;
return this.#connect();
}
throw new Error("Failed to connect to database");
}
}
async query(sql, params) {
if (!this.#connection) {
await this.#connect();
}
// Implementación de la consulta
return results;
}
}
// Solo exportamos una instancia, no la clase ni la cadena de conexión
export const db = new DatabaseConnection();
Este enfoque proporciona:
- Encapsulación a nivel de módulo para la cadena de conexión
- Encapsulación a nivel de clase para los detalles de implementación
- Una API pública mínima (solo el método query)
- Patrón singleton para la conexión a la base de datos
WeakMaps para datos privados (enfoque alternativo)
Antes de los campos privados nativos, los WeakMaps ofrecían una solución elegante para almacenar datos privados:
// Enfoque alternativo para entornos que no soportan campos privados
const privateData = new WeakMap();
class User {
constructor(name, role) {
privateData.set(this, {
name,
role,
loginAttempts: 0
});
}
authenticate(password) {
const data = privateData.get(this);
if (password !== "correct-password") {
data.loginAttempts++;
if (data.loginAttempts >= 3) {
return { success: false, locked: true };
}
return { success: false, attempts: data.loginAttempts };
}
data.loginAttempts = 0;
return {
success: true,
userData: { name: data.name, role: data.role }
};
}
}
Esta técnica sigue siendo útil en ciertos contextos, especialmente cuando se necesita compatibilidad con navegadores antiguos o cuando se trabaja con objetos que no son instancias de clases.
Las técnicas modernas de encapsulación en JavaScript proporcionan mecanismos robustos para proteger la integridad de los datos y crear APIs limpias y bien definidas. Los campos privados con #
y el sistema de módulos ESM representan un avance significativo en la madurez del lenguaje para la programación orientada a objetos.
Encapsulación en arquitecturas complejas: Interfaces públicas, inmutabilidad y contratos de diseño
Cuando desarrollamos sistemas de software complejos, la encapsulación va más allá de simplemente ocultar datos. Se convierte en una herramienta arquitectónica fundamental que define cómo interactúan los diferentes componentes de un sistema. En arquitecturas modernas de JavaScript, la encapsulación se manifiesta a través de interfaces bien definidas, inmutabilidad y contratos de diseño explícitos.
Interfaces públicas estables
Una interfaz pública bien diseñada actúa como un contrato entre el componente y sus consumidores, permitiendo que la implementación interna evolucione sin afectar al código cliente:
// Módulo que expone una interfaz pública estable
export class UserRepository {
// La interfaz pública se mantiene estable
async findById(id) { /* implementación */ }
async findByEmail(email) { /* implementación */ }
async save(user) { /* implementación */ }
async delete(id) { /* implementación */ }
// Los métodos privados pueden cambiar libremente
#validateUser(user) { /* implementación */ }
#normalizeEmail(email) { /* implementación */ }
}
Las interfaces públicas efectivas siguen estos principios clave:
- Minimalismo: Exponer solo lo estrictamente necesario
- Consistencia: Mantener patrones coherentes en nombres y comportamientos
- Completitud: Proporcionar todas las operaciones necesarias para usar el componente
- Estabilidad: Cambiar con poca frecuencia para no romper el código cliente
Inmutabilidad como forma de encapsulación
La inmutabilidad es una técnica poderosa de encapsulación que garantiza que los objetos no cambien después de su creación, eliminando efectos secundarios inesperados:
class ImmutablePoint {
#x;
#y;
constructor(x, y) {
this.#x = x;
this.#y = y;
// Congelamos el objeto para prevenir la adición de propiedades
Object.freeze(this);
}
get x() { return this.#x; }
get y() { return this.#y; }
// En lugar de modificar, creamos nuevas instancias
translate(dx, dy) {
return new ImmutablePoint(this.#x + dx, this.#y + dy);
}
scale(factor) {
return new ImmutablePoint(this.#x * factor, this.#y * factor);
}
}
Los objetos inmutables ofrecen varias ventajas arquitectónicas:
- Seguridad en concurrencia: No hay riesgo de modificaciones simultáneas
- Razonamiento simplificado: El estado no cambia inesperadamente
- Facilidad para testing: Comportamiento predecible y reproducible
- Compatibilidad con patrones funcionales: Facilita composición y transformaciones
Contratos de diseño explícitos
Los contratos de diseño formalizan las expectativas sobre cómo debe usarse un componente, reforzando la encapsulación mediante validaciones explícitas:
class BankAccount {
#balance = 0;
#minimumBalance;
#owner;
constructor(owner, initialDeposit = 0, minimumBalance = 0) {
// Precondiciones - validamos antes de crear el objeto
if (typeof owner !== 'object' || !owner.id || !owner.name) {
throw new TypeError('Owner must be an object with id and name');
}
if (initialDeposit < minimumBalance) {
throw new RangeError(`Initial deposit must be at least ${minimumBalance}`);
}
this.#owner = { ...owner }; // Copia defensiva
this.#balance = initialDeposit;
this.#minimumBalance = minimumBalance;
}
withdraw(amount) {
// Precondiciones
if (amount <= 0) {
throw new RangeError('Withdrawal amount must be positive');
}
// Invariantes
if (this.#balance - amount < this.#minimumBalance) {
throw new Error('Insufficient funds');
}
this.#balance -= amount;
// Postcondiciones
return {
success: true,
newBalance: this.#balance,
timestamp: new Date()
};
}
}
Los contratos de diseño incluyen:
- Precondiciones: Requisitos que deben cumplirse antes de ejecutar una operación
- Postcondiciones: Garantías sobre el estado después de la operación
- Invariantes: Condiciones que siempre deben ser verdaderas durante la vida del objeto
Patrones de fachada para sistemas complejos
El patrón fachada proporciona una interfaz simplificada a un subsistema complejo, encapsulando la complejidad interna:
// Subsistemas complejos encapsulados
class PaymentProcessor { /* implementación compleja */ }
class InventoryManager { /* implementación compleja */ }
class ShippingService { /* implementación compleja */ }
class NotificationSystem { /* implementación compleja */ }
// Fachada que simplifica el uso del sistema
export class OrderService {
#paymentProcessor = new PaymentProcessor();
#inventory = new InventoryManager();
#shipping = new ShippingService();
#notifications = new NotificationSystem();
async placeOrder(orderData) {
try {
// Validación de la orden
this.#validateOrder(orderData);
// Reserva de inventario
await this.#inventory.reserveItems(orderData.items);
// Procesamiento del pago
const paymentResult = await this.#paymentProcessor.processPayment(
orderData.payment
);
if (!paymentResult.success) {
await this.#inventory.releaseItems(orderData.items);
return { success: false, reason: 'payment_failed' };
}
// Creación del envío
const shipment = await this.#shipping.createShipment(
orderData.items,
orderData.shippingAddress
);
// Notificación al cliente
await this.#notifications.sendOrderConfirmation(
orderData.customer,
{ orderId: shipment.trackingId }
);
return {
success: true,
orderId: shipment.trackingId,
estimatedDelivery: shipment.estimatedDelivery
};
} catch (error) {
// Manejo de errores y compensación
return { success: false, reason: error.message };
}
}
#validateOrder(orderData) {
// Implementación de validación
}
}
Este patrón:
- Simplifica la API expuesta a los clientes
- Oculta las dependencias y relaciones entre subsistemas
- Centraliza la lógica de coordinación entre componentes
- Reduce el acoplamiento entre el cliente y los subsistemas
Encapsulación a nivel de dominio con objetos de valor
Los objetos de valor encapsulan conceptos del dominio, garantizando su integridad y validez:
class EmailAddress {
#value;
constructor(email) {
if (!this.#isValid(email)) {
throw new Error('Invalid email format');
}
this.#value = email.toLowerCase();
}
#isValid(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
get value() {
return this.#value;
}
equals(other) {
return other instanceof EmailAddress && other.value === this.#value;
}
toString() {
return this.#value;
}
}
class Money {
#amount;
#currency;
constructor(amount, currency) {
if (typeof amount !== 'number' || isNaN(amount)) {
throw new TypeError('Amount must be a number');
}
if (typeof currency !== 'string' || currency.length !== 3) {
throw new TypeError('Currency must be a 3-letter ISO code');
}
this.#amount = amount;
this.#currency = currency.toUpperCase();
}
get amount() { return this.#amount; }
get currency() { return this.#currency; }
add(other) {
if (!(other instanceof Money) || other.currency !== this.#currency) {
throw new Error('Cannot add different currencies');
}
return new Money(this.#amount + other.amount, this.#currency);
}
multiply(factor) {
return new Money(this.#amount * factor, this.#currency);
}
toString() {
return `${this.#amount.toFixed(2)} ${this.#currency}`;
}
}
Los objetos de valor:
- Encapsulan reglas de validación específicas del dominio
- Son inmutables por diseño
- Tienen igualdad basada en valores (no en identidad)
- Expresan intención más claramente que tipos primitivos
Composición sobre herencia para encapsulación flexible
La composición ofrece una forma más flexible de encapsulación que la herencia, permitiendo combinar comportamientos sin exponer la implementación interna:
// Comportamientos encapsulados como objetos independientes
class Logger {
log(message) {
console.log(`[LOG] ${message}`);
}
}
class EventEmitter {
#listeners = new Map();
on(event, callback) {
if (!this.#listeners.has(event)) {
this.#listeners.set(event, []);
}
this.#listeners.get(event).push(callback);
}
emit(event, data) {
const callbacks = this.#listeners.get(event) || [];
callbacks.forEach(callback => callback(data));
}
}
class HttpClient {
async get(url) { /* implementación */ }
async post(url, data) { /* implementación */ }
}
// Componente que utiliza composición para encapsular comportamientos
class UserService {
#logger = new Logger();
#events = new EventEmitter();
#http = new HttpClient();
async getUser(id) {
this.#logger.log(`Fetching user ${id}`);
try {
const user = await this.#http.get(`/users/${id}`);
this.#events.emit('user:loaded', user);
return user;
} catch (error) {
this.#logger.log(`Error fetching user: ${error.message}`);
this.#events.emit('user:error', { id, error });
throw error;
}
}
onUserLoaded(callback) {
this.#events.on('user:loaded', callback);
}
}
La composición:
- Encapsula comportamientos en unidades independientes
- Permite cambiar implementaciones sin afectar la interfaz
- Facilita la prueba unitaria de cada componente
- Evita los problemas de la herencia como el acoplamiento rígido
Encapsulación en arquitecturas de microservicios frontend
En arquitecturas frontend modernas, la encapsulación se extiende a componentes autónomos que encapsulan su estado, lógica y presentación:
// Componente encapsulado con estado interno y API pública
class ShoppingCart extends HTMLElement {
#items = [];
#root;
constructor() {
super();
this.#root = this.attachShadow({ mode: 'closed' });
this.#render();
}
// API pública
addItem(product, quantity = 1) {
const existingItem = this.#items.find(item => item.id === product.id);
if (existingItem) {
existingItem.quantity += quantity;
} else {
this.#items.push({ ...product, quantity });
}
this.#render();
this.dispatchEvent(new CustomEvent('cart:changed', {
detail: { itemCount: this.itemCount }
}));
}
removeItem(productId) {
this.#items = this.#items.filter(item => item.id !== productId);
this.#render();
this.dispatchEvent(new CustomEvent('cart:changed', {
detail: { itemCount: this.itemCount }
}));
}
get itemCount() {
return this.#items.reduce((total, item) => total + item.quantity, 0);
}
get total() {
return this.#items.reduce(
(sum, item) => sum + (item.price * item.quantity),
0
);
}
// Métodos privados
#render() {
this.#root.innerHTML = `
<style>/* Estilos encapsulados */</style>
<div class="cart">
<h2>Your Cart (${this.itemCount} items)</h2>
<ul>
${this.#renderItems()}
</ul>
<div class="total">Total: $${this.total.toFixed(2)}</div>
</div>
`;
// Agregar event listeners
this.#attachEventListeners();
}
#renderItems() {
return this.#items.map(item => `
<li data-id="${item.id}">
${item.name} - $${item.price} × ${item.quantity}
<button class="remove">×</button>
</li>
`).join('');
}
#attachEventListeners() {
const removeButtons = this.#root.querySelectorAll('.remove');
removeButtons.forEach(button => {
const li = button.closest('li');
button.addEventListener('click', () => {
this.removeItem(li.dataset.id);
});
});
}
}
// Registrar el componente
customElements.define('shopping-cart', ShoppingCart);
Este enfoque de encapsulación:
- Utiliza Shadow DOM para encapsular el DOM interno
- Mantiene el estado privado inaccesible desde el exterior
- Expone una API pública bien definida
- Comunica cambios mediante eventos personalizados
La encapsulación en arquitecturas complejas de JavaScript va más allá del simple ocultamiento de datos, convirtiéndose en un principio arquitectónico que define cómo se estructuran, comunican y evolucionan los componentes de un sistema. Mediante interfaces públicas estables, inmutabilidad, contratos de diseño explícitos y patrones como fachada y composición, podemos construir sistemas robustos, mantenibles y adaptables a los cambios.
Ejercicios de esta lección Encapsulación
Evalúa tus conocimientos de esta lección Encapsulación 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 encapsulación en la programación orientada a objetos.
- Conocer las ventajas de aplicar la encapsulación en la organización y protección de la información interna de los objetos.
- Aprender cómo implementar la encapsulación en JavaScript utilizando diferentes enfoques como IIFE, clausuras, objetos literales y clases con getters y setters.
- Entender la diferencia entre propiedades y métodos públicos y privados, y cómo limitar el acceso a la información interna de los objetos.
- Conocer los pros y contras de los enfoques de encapsulación en JavaScript y saber cuándo utilizar cada uno según las necesidades del proyecto.
- Saber cómo la encapsulación mejora la modularidad y la mantenibilidad del código, y cómo evita la manipulación involuntaria de datos internos.