JavaScript

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ícate

Fundamentos 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:

  1. Ocultamiento de datos: Restringir el acceso directo a ciertos componentes del objeto
  2. 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
Aprende JavaScript online

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

JavaScript
Código

Uso de operadores

JavaScript
Puzzle

Uso de operadores

JavaScript
Test

Estructuras de control

JavaScript
Test

Proyecto Manipulación DOM

JavaScript
Proyecto

Excepciones

JavaScript
Test

Transformación con map()

JavaScript
Código

Arrays y Métodos

JavaScript
Código

Reto Métodos de Strings

JavaScript
Código

Transformación con map()

JavaScript
Puzzle

Funciones flecha

JavaScript
Test

Async / Await

JavaScript
Código

Selección de elementos DOM

JavaScript
Puzzle

API Fetch

JavaScript
Código

Encapsulación

JavaScript
Test

Mapas con Map

JavaScript
Código

Creación y uso de variables

JavaScript
Puzzle

Polimorfismo

JavaScript
Puzzle

Reto Funciones flecha

JavaScript
Código

Tipos de datos

JavaScript
Puzzle

Reto Operadores avanzados

JavaScript
Código

Promises

JavaScript
Código

Reto Estructuras de control

JavaScript
Código

Estructuras de control

JavaScript
Puzzle

Pruebas unitarias

JavaScript
Proyecto

Inmutabilidad y programación funcional pura

JavaScript
Código

Funciones flecha

JavaScript
Puzzle

Polimorfismo

JavaScript
Test

Reto Polimorfismo

JavaScript
Código

Array

JavaScript
Código

Transformación con map()

JavaScript
Test

Reto Variables

JavaScript
Código

Gestor de tareas con JavaScript

JavaScript
Proyecto

Proyecto Modificación de elementos DOM

JavaScript
Proyecto

Manipulación DOM

JavaScript
Test

Funciones

JavaScript
Test

Conjuntos con Set

JavaScript
Código

Reto Prototipos y cadena de prototipos

JavaScript
Código

Reto Encapsulación

JavaScript
Código

Funciones flecha

JavaScript
Código

Async / Await

JavaScript
Código

Reto Excepciones

JavaScript
Código

Reto Filtrado con filter() y find()

JavaScript
Código

Creación y uso de variables

JavaScript
Test

Excepciones

JavaScript
Puzzle

Promises

JavaScript
Código

Funciones cierre (closure)

JavaScript
Test

Reto Herencia

JavaScript
Código

Herencia

JavaScript
Puzzle

Proyecto Eventos del DOM

JavaScript
Proyecto

Herencia

JavaScript
Test

Selección de elementos DOM

JavaScript
Test

Modificación de elementos DOM

JavaScript
Test

Reto Clases y objetos

JavaScript
Código

Filtrado con filter() y find()

JavaScript
Test

Funciones cierre (closure)

JavaScript
Puzzle

Reto Destructuring de objetos y arrays

JavaScript
Código

Callbacks

JavaScript
Código

Funciones

JavaScript
Puzzle

Mapas con Map

JavaScript
Test

Reducción con reduce()

JavaScript
Test

Callbacks

JavaScript
Puzzle

Manipulación DOM

JavaScript
Puzzle

Introducción al DOM

JavaScript
Proyecto

Reto Funciones

JavaScript
Código

Reto Funciones cierre (closure)

JavaScript
Código

Promises

JavaScript
Test

Reto Reducción con reduce()

JavaScript
Código

Async / Await

JavaScript
Test

Reto Estructuras de control

JavaScript
Código

Eventos del DOM

JavaScript
Puzzle

Introducción a JavaScript

JavaScript
Puzzle

Async / Await

JavaScript
Puzzle

Promises

JavaScript
Puzzle

Selección de elementos DOM

JavaScript
Proyecto

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

Proyecto carrito compra agoodshop

JavaScript
Proyecto

Introducción a JavaScript

JavaScript
Test

Reto Mapas con Map

JavaScript
Código

Funciones

JavaScript
Código

Proyecto administrador de contactos

JavaScript
Proyecto

Reto Expresiones regulares

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

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

JavaScript

Introducción Y Entorno

Tipos De Datos

JavaScript

Sintaxis

Variables

JavaScript

Sintaxis

Operadores

JavaScript

Sintaxis

Estructuras De Control

JavaScript

Sintaxis

Funciones

JavaScript

Sintaxis

Funciones Cierre (Closure)

JavaScript

Sintaxis

Métodos De Strings

JavaScript

Sintaxis

Funciones Cierre (Closure)

JavaScript

Sintaxis

Operadores Avanzados

JavaScript

Sintaxis

Funciones

JavaScript

Sintaxis

Expresiones Regulares

JavaScript

Sintaxis

Estructuras De Control

JavaScript

Sintaxis

Arrays Y Métodos

JavaScript

Estructuras De Datos

Conjuntos Con Set

JavaScript

Estructuras De Datos

Mapas Con Map

JavaScript

Estructuras De Datos

Conjuntos Con Set

JavaScript

Estructuras De Datos

Funciones Flecha

JavaScript

Programación Funcional

Filtrado Con Filter() Y Find()

JavaScript

Programación Funcional

Transformación Con Map()

JavaScript

Programación Funcional

Reducción Con Reduce()

JavaScript

Programación Funcional

Funciones Flecha

JavaScript

Programación Funcional

Transformación Con Map()

JavaScript

Programación Funcional

Inmutabilidad Y Programación Funcional Pura

JavaScript

Programación Funcional

Clases Y Objetos

JavaScript

Programación Orientada A Objetos

Excepciones

JavaScript

Programación Orientada A Objetos

Encapsulación

JavaScript

Programación Orientada A Objetos

Herencia

JavaScript

Programación Orientada A Objetos

Polimorfismo

JavaScript

Programación Orientada A Objetos

This Y Contexto

JavaScript

Programación Orientada A Objetos

Patrón De Módulos Y Namespace

JavaScript

Programación Orientada A Objetos

Prototipos Y Cadena De Prototipos

JavaScript

Programación Orientada A Objetos

Destructuring De Objetos Y Arrays

JavaScript

Programación Orientada A Objetos

Manipulación Dom

JavaScript

Dom

Selección De Elementos Dom

JavaScript

Dom

Modificación De Elementos Dom

JavaScript

Dom

Eventos Del Dom

JavaScript

Dom

Localstorage Y Sessionstorage

JavaScript

Dom

Bom (Browser Object Model)

JavaScript

Dom

Callbacks

JavaScript

Programación Asíncrona

Promises

JavaScript

Programación Asíncrona

Async / Await

JavaScript

Programación Asíncrona

Promises

JavaScript

Programación Asíncrona

Api Fetch

JavaScript

Programación Asíncrona

Async / Await

JavaScript

Programación Asíncrona

Naturaleza De Js Y Event Loop

JavaScript

Programación Asíncrona

Callbacks

JavaScript

Programación Asíncrona

Websockets

JavaScript

Programación Asíncrona

Módulos En Es6

JavaScript

Construcción

Configuración De Bundlers Como Vite

JavaScript

Construcción

Eslint Y Calidad De Código

JavaScript

Construcción

Npm Y Dependencias

JavaScript

Construcción

Introducción A Pruebas En Js

JavaScript

Testing

Pruebas Unitarias

JavaScript

Testing

Accede GRATIS a JavaScript y certifícate

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.