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 aprendimos junto con la herencia y el polimorfismo. Este principio permite ocultar los detalles internos de implementación de un objeto y exponer solo aquellas partes que son necesarias para su uso.
¿Qué es 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 incorrectas 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
Esta técnica es muy común en código JavaScript y es importante reconocerla, aunque no proporciona una verdadera 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);
}
Este enfoque combina de manera efectiva la sintaxis de clase que aprendimos anteriormente con mecanismos para controlar el acceso a datos.
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 BankAccount {
#balance;
#transactionHistory;
constructor(initialBalance = 0) {
this.#balance = initialBalance;
this.#transactionHistory = [];
if (initialBalance > 0) {
this.#addTransaction("initial deposit", initialBalance);
}
}
deposit(amount) {
if (amount <= 0) {
throw new Error("Deposit amount must be positive");
}
this.#balance += amount;
this.#addTransaction("deposit", amount);
return this.#balance;
}
withdraw(amount) {
if (amount <= 0) {
throw new Error("Withdrawal amount must be positive");
}
if (amount > this.#balance) {
throw new Error("Insufficient funds");
}
this.#balance -= amount;
this.#addTransaction("withdrawal", amount);
return this.#balance;
}
#addTransaction(type, amount) {
this.#transactionHistory.push({
type,
amount,
date: new Date()
});
}
getBalance() {
return this.#balance;
}
getTransactionHistory() {
// Devolvemos una copia para evitar modificaciones externas
return [...this.#transactionHistory];
}
}
A diferencia de las convenciones con guion bajo, los campos privados con #
están estrictamente encapsulados:
const account = new BankAccount(1000);
console.log(account.getBalance()); // 1000
account.deposit(500);
account.withdraw(200);
console.log(account.getBalance()); // 1300
// Intentos de acceso directo generan errores
try {
console.log(account.#balance); // 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
Aunque los campos privados con #
son una característica relativamente reciente, proporcionan el mecanismo de encapsulación más robusto en JavaScript actual. Su soporte en navegadores modernos es bueno, pero para entornos más antiguos, es recomendable utilizar las técnicas alternativas que hemos visto.
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
Patrón de módulo con IIFE (Immediately Invoked Function Expression)
Este patrón, que utiliza closures, ha sido una técnica estándar para encapsulación en JavaScript durante muchos años:
const calculator = (function() {
// Variables privadas
let result = 0;
// Funciones privadas
function validate(n) {
if (typeof n !== 'number') {
throw new Error('Only numbers are allowed');
}
}
// API pública
return {
add(n) {
validate(n);
result += n;
return this;
},
subtract(n) {
validate(n);
result -= n;
return this;
},
multiply(n) {
validate(n);
result *= n;
return this;
},
divide(n) {
validate(n);
if (n === 0) {
throw new Error('Cannot divide by zero');
}
result /= n;
return this;
},
getResult() {
return result;
},
clear() {
result = 0;
return this;
}
};
})();
// Uso
calculator.add(5).multiply(2).subtract(3).divide(2);
console.log(calculator.getResult()); // 3.5
Este patrón crea un ámbito privado donde podemos definir variables y funciones que no son accesibles desde el exterior, exponiendo solo una API pública bien definida.
WeakMaps para datos privados (enfoque alternativo)
Antes de los campos privados nativos, los WeakMaps ofrecían una solución elegante para almacenar datos privados:
// Una alternativa para entornos que no soportan campos privados
const privateData = new WeakMap();
class Person {
constructor(name, age) {
privateData.set(this, {
name,
age
});
}
getName() {
return privateData.get(this).name;
}
getAge() {
return privateData.get(this).age;
}
setAge(age) {
if (age < 0 || age > 120) {
throw new Error('Invalid age');
}
privateData.get(this).age = age;
}
celebrateBirthday() {
const data = privateData.get(this);
data.age += 1;
return `Happy ${data.age}th birthday, ${data.name}!`;
}
}
const person = new Person("Alice", 30);
console.log(person.getName()); // "Alice"
console.log(person.getAge()); // 30
console.log(person.celebrateBirthday()); // "Happy 31th birthday, Alice!"
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.
Módulos ES (ECMAScript Modules)
El sistema de módulos nativo de JavaScript proporciona otra capa de encapsulación a nivel de archivo:
// userService.js
// Variables privadas al módulo
const API_KEY = "secret_api_key";
const BASE_URL = "https://api.example.com";
// Función privada
function validateEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
// Funciones exportadas (API pública)
export function createUser(userData) {
if (!validateEmail(userData.email)) {
throw new Error("Invalid email format");
}
// Uso interno de variables privadas
return fetch(`${BASE_URL}/users`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify(userData)
}).then(res => res.json());
}
export function getUser(id) {
return fetch(`${BASE_URL}/users/${id}`, {
headers: {
"Authorization": `Bearer ${API_KEY}`
}
}).then(res => res.json());
}
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, BASE_URL 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
- Organizar el código en unidades cohesivas
Aplicaciones prácticas de la encapsulación: Integrando encapsulación en nuestros diseños orientados a objetos
La encapsulación no es solo un concepto teórico, sino una herramienta práctica para crear código más mantenible, seguro y flexible. Veamos cómo integrar la encapsulación con los conceptos de POO que hemos aprendido.
Encapsulación e invariantes de clase
La encapsulación nos permite mantener invariantes de clase - condiciones que siempre deben ser verdaderas para un objeto. Por ejemplo, un círculo siempre debe tener un radio positivo:
class Circle {
#radius;
constructor(radius) {
this.setRadius(radius);
}
// Getter
getRadius() {
return this.#radius;
}
// Setter con validación
setRadius(value) {
if (typeof value !== 'number' || value <= 0) {
throw new Error('Radius must be a positive number');
}
this.#radius = value;
}
// Métodos que dependen del estado interno
getArea() {
return Math.PI * this.#radius * this.#radius;
}
getCircumference() {
return 2 * Math.PI * this.#radius;
}
}
const circle = new Circle(5);
console.log(circle.getArea()); // ~78.54
circle.setRadius(10);
console.log(circle.getCircumference()); // ~62.83
try {
circle.setRadius(-5); // Error: Radius must be a positive number
} catch (e) {
console.log(e.message);
}
La encapsulación del radio permite que la clase mantenga su invariante (radio positivo) y evita que el objeto llegue a un estado inválido.
Encapsulación en herencia
La encapsulación funciona de manera interesante con la herencia. Las clases derivadas no pueden acceder a los campos privados de la clase base:
class Shape {
#color;
constructor(color) {
this.#color = color;
}
getColor() {
return this.#color;
}
setColor(color) {
this.#color = color;
}
// Método protegido (por convención) que las subclases deberían implementar
_calculateArea() {
throw new Error('_calculateArea() must be implemented by subclasses');
}
// Método público que utiliza el método protegido
getArea() {
return this._calculateArea();
}
}
class Rectangle extends Shape {
#width;
#height;
constructor(color, width, height) {
super(color);
this.#width = width;
this.#height = height;
}
// Implementación del método protegido
_calculateArea() {
return this.#width * this.#height;
}
// Getters y setters específicos
getWidth() { return this.#width; }
setWidth(width) { this.#width = width; }
getHeight() { return this.#height; }
setHeight(height) { this.#height = height; }
}
const rect = new Rectangle('red', 5, 3);
console.log(rect.getColor()); // 'red'
console.log(rect.getArea()); // 15
En este ejemplo vemos:
- La clase base
Shape
tiene un campo privado#color
y un método protegido (por convención)_calculateArea()
- La clase derivada
Rectangle
implementa el método protegido y añade sus propios campos privados - La herencia funciona correctamente mientras cada clase encapsula sus propios detalles internos
Encapsulación para modelos de dominio
La encapsulación es especialmente útil para implementar modelos de dominio que encapsulan reglas de negocio:
class ShoppingCart {
#items = [];
#taxRate = 0.21; // 21% de IVA
addItem(product, quantity = 1) {
if (quantity <= 0) {
throw new Error('Quantity must be positive');
}
const existingItem = this.#items.find(item => item.product.id === product.id);
if (existingItem) {
existingItem.quantity += quantity;
} else {
this.#items.push({ product, quantity });
}
}
removeItem(productId) {
const index = this.#items.findIndex(item => item.product.id === productId);
if (index !== -1) {
this.#items.splice(index, 1);
}
}
updateQuantity(productId, quantity) {
if (quantity <= 0) {
return this.removeItem(productId);
}
const item = this.#items.find(item => item.product.id === productId);
if (item) {
item.quantity = quantity;
}
}
getItems() {
// Devolvemos una copia para prevenir modificaciones directas
return [...this.#items];
}
getSubtotal() {
return this.#items.reduce(
(sum, item) => sum + (item.product.price * item.quantity),
0
);
}
getTaxAmount() {
return this.getSubtotal() * this.#taxRate;
}
getTotal() {
return this.getSubtotal() + this.getTaxAmount();
}
getItemCount() {
return this.#items.reduce((count, item) => count + item.quantity, 0);
}
}
// Uso
const cart = new ShoppingCart();
cart.addItem({ id: 1, name: 'Laptop', price: 999.99 });
cart.addItem({ id: 2, name: 'Mouse', price: 29.99 }, 2);
console.log(`Items in cart: ${cart.getItemCount()}`);
console.log(`Subtotal: ${cart.getSubtotal().toFixed(2)} €`);
console.log(`Taxes: ${cart.getTaxAmount().toFixed(2)} €`);
console.log(`Total: ${cart.getTotal().toFixed(2)} €`);
Esta implementación encapsula:
- La estructura interna de almacenamiento de items (
#items
) - La tasa de impuesto (
#taxRate
) - Las reglas de negocio (como el manejo de cantidades no válidas)
- Los cálculos para subtotal, impuestos y total
Encapsulación para objetos de valor inmutables
La encapsulación combinada con inmutabilidad nos permite crear objetos de valor robustos:
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();
// Congelar la instancia para prevenir la adición de propiedades
Object.freeze(this);
}
get amount() { return this.#amount; }
get currency() { return this.#currency; }
// Operaciones que devuelven nuevos objetos en lugar de modificar el actual
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);
}
subtract(other) {
if (!(other instanceof Money) || other.currency !== this.#currency) {
throw new Error('Cannot subtract 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}`;
}
}
// Uso
const price = new Money(19.99, 'EUR');
const taxAmount = price.multiply(0.21);
const total = price.add(taxAmount);
console.log(`Price: ${price}`); // "19.99 EUR"
console.log(`Tax: ${taxAmount}`); // "4.20 EUR"
console.log(`Total: ${total}`); // "24.19 EUR"
Esta implementación combina encapsulación con inmutabilidad, lo que hace que los objetos Money
sean:
- Seguros para usar en entornos concurrentes
- Fáciles de razonar sobre su estado
- Menos propensos a errores debido a efectos secundarios
Conectando encapsulación con otros principios de POO
La encapsulación trabaja en conjunto con otros principios de POO:
// Clase base abstracta que define una interfaz
class PaymentMethod {
// Métodos abstractos que las subclases deben implementar
processPayment(amount) {
throw new Error('Method not implemented');
}
refundPayment(transactionId) {
throw new Error('Method not implemented');
}
}
// Implementación concreta con detalles encapsulados
class CreditCardPayment extends PaymentMethod {
#apiKey;
#merchantId;
#transactions = new Map();
constructor(apiKey, merchantId) {
super();
this.#apiKey = apiKey;
this.#merchantId = merchantId;
}
// Implementación del método de la interfaz
processPayment(amount) {
// En un caso real, esto llamaría a una API de pago
console.log(`Processing credit card payment of ${amount.toFixed(2)} €`);
const transactionId = this.#generateTransactionId();
this.#transactions.set(transactionId, {
amount,
timestamp: new Date(),
status: 'completed'
});
return {
success: true,
transactionId
};
}
// Implementación del método de la interfaz
refundPayment(transactionId) {
if (!this.#transactions.has(transactionId)) {
throw new Error(`Transaction ${transactionId} not found`);
}
const transaction = this.#transactions.get(transactionId);
console.log(`Refunding ${transaction.amount.toFixed(2)} € to credit card`);
transaction.status = 'refunded';
return {
success: true,
refundedAmount: transaction.amount
};
}
// Método privado
#generateTransactionId() {
return 'txn_' + Math.random().toString(36).substr(2, 9);
}
}
// Otro implementador de la misma interfaz
class PayPalPayment extends PaymentMethod {
#clientId;
#clientSecret;
#transactions = [];
constructor(clientId, clientSecret) {
super();
this.#clientId = clientId;
this.#clientSecret = clientSecret;
}
processPayment(amount) {
console.log(`Processing PayPal payment of ${amount.toFixed(2)} €`);
// Implementación específica...
// ...
}
refundPayment(transactionId) {
console.log(`Refunding PayPal transaction ${transactionId}`);
// Implementación específica...
// ...
}
}
// Cliente que utiliza el polimorfismo sin conocer los detalles internos
function checkout(cart, paymentMethod) {
const amount = cart.getTotal();
return paymentMethod.processPayment(amount);
}
// Uso
const cart = new ShoppingCart();
cart.addItem({ id: 1, name: 'Laptop', price: 999.99 });
const ccPayment = new CreditCardPayment('sk_test_123', 'merch_456');
const paypalPayment = new PayPalPayment('client123', 'secret456');
// Mismo método, diferentes implementaciones encapsuladas
const ccResult = checkout(cart, ccPayment);
const ppResult = checkout(cart, paypalPayment);
Este ejemplo muestra cómo la encapsulación trabaja junto con:
- Herencia: Las clases derivadas heredan la interfaz pero encapsulan su propia implementación
- Polimorfismo: Diferentes implementaciones pueden usarse intercambiablemente
- Abstracción: Los clientes solo ven la interfaz, no los detalles internos
Otros ejercicios de programación de JavaScript
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.
Clases y objetos
Uso de operadores
Uso de operadores
Estructuras de control
Proyecto Manipulación DOM
Excepciones
Transformación con map()
Arrays y Métodos
Reto Métodos de Strings
Transformación con map()
Funciones flecha
Async / Await
Selección de elementos DOM
API Fetch
Encapsulación
Mapas con Map
Creación y uso de variables
Polimorfismo
Reto Funciones flecha
Tipos de datos
Reto Operadores avanzados
Promises
Reto Estructuras de control
Estructuras de control
Pruebas unitarias
Inmutabilidad y programación funcional pura
Funciones flecha
Polimorfismo
Reto Polimorfismo
Array
Transformación con map()
Reto Variables
Gestor de tareas con JavaScript
Proyecto Modificación de elementos DOM
Manipulación DOM
Funciones
Conjuntos con Set
Reto Prototipos y cadena de prototipos
Reto Encapsulación
Funciones flecha
Async / Await
Reto Excepciones
Reto Filtrado con filter() y find()
Creación y uso de variables
Excepciones
Promises
Funciones cierre (closure)
Reto Herencia
Herencia
Proyecto Eventos del DOM
Herencia
Selección de elementos DOM
Modificación de elementos DOM
Reto Clases y objetos
Filtrado con filter() y find()
Funciones cierre (closure)
Reto Destructuring de objetos y arrays
Callbacks
Funciones
Mapas con Map
Reducción con reduce()
Callbacks
Manipulación DOM
Introducción al DOM
Reto Funciones
Reto Funciones cierre (closure)
Promises
Reto Reducción con reduce()
Async / Await
Reto Estructuras de control
Eventos del DOM
Introducción a JavaScript
Async / Await
Promises
Selección de elementos DOM
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
Proyecto carrito compra agoodshop
Introducción a JavaScript
Reto Mapas con Map
Funciones
Proyecto administrador de contactos
Reto Expresiones regulares
Tipos de datos
Clases y objetos
Array
Conjuntos con Set
Array
Encapsulación
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
Métodos De Strings
Sintaxis
Funciones Cierre (Closure)
Sintaxis
Operadores Avanzados
Sintaxis
Funciones
Sintaxis
Expresiones Regulares
Sintaxis
Estructuras De Control
Sintaxis
Arrays Y Métodos
Estructuras De Datos
Conjuntos Con Set
Estructuras De Datos
Mapas Con Map
Estructuras De Datos
Conjuntos Con Set
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
Funciones Flecha
Programación Funcional
Transformación Con Map()
Programación Funcional
Inmutabilidad Y Programación Funcional Pura
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
This Y Contexto
Programación Orientada A Objetos
Patrón De Módulos Y Namespace
Programación Orientada A Objetos
Prototipos Y Cadena De Prototipos
Programación Orientada A Objetos
Destructuring De Objetos Y Arrays
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
Localstorage Y Sessionstorage
Dom
Bom (Browser Object Model)
Dom
Callbacks
Programación Asíncrona
Promises
Programación Asíncrona
Async / Await
Programación Asíncrona
Promises
Programación Asíncrona
Api Fetch
Programación Asíncrona
Async / Await
Programación Asíncrona
Naturaleza De Js Y Event Loop
Programación Asíncrona
Callbacks
Programación Asíncrona
Websockets
Programación Asíncrona
Módulos En Es6
Construcción
Configuración De Bundlers Como Vite
Construcción
Eslint Y Calidad De Código
Construcción
Npm Y Dependencias
Construcción
Introducción A Pruebas En Js
Testing
Pruebas Unitarias
Testing
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.