Command Palette

Search for a command to run...

Campos Privados (#field): Encapsulación en JavaScript

Aprende a usar campos privados con el prefijo # para encapsular datos y proteger la implementación interna de tus clases en JavaScript moderno.

Lectura: 16 min
Nivel: Intermedio

TL;DR - Resumen rápido

  • Los campos privados se definen con el prefijo # antes del nombre
  • Los campos privados solo son accesibles dentro de la clase donde se definen
  • Los campos privados no se heredan y no son accesibles desde fuera
  • Puedes crear métodos privados usando el prefijo #
  • Los campos privados proporcionan verdadera encapsulación en JavaScript
  • Los campos privados son una característica de ES2022

Introducción a Campos Privados

Los campos privados son una característica de JavaScript introducida en ES2022 que te permite definir propiedades y métodos que solo son accesibles dentro de la clase donde se definen. A diferencia de la convención de usar prefijo _ (que es solo una señal visual), los campos privados con prefijo # proporcionan verdadera encapsulación a nivel de lenguaje, haciendo físicamente imposible acceder a estos campos desde fuera de la clase.

Los campos privados son especialmente útiles para proteger datos sensibles, mantener invariantes de la clase, ocultar detalles de implementación y prevenir modificaciones accidentales o maliciosas del estado interno. Una vez definido un campo como privado, solo los métodos de la clase pueden acceder a él, garantizando control total sobre cómo se manipulan los datos.

Encapsulación verdadera

Antes de ES2022, los desarrolladores usaban convenciones como el prefijo _ para indicar propiedades privadas, pero estas seguían siendo accesibles. Los campos privados con # proporcionan verdadera encapsulación a nivel de lenguaje que el runtime de JavaScript hace cumplir.

Sintaxis de Campos Privados

Los campos privados se definen con el prefijo # antes del nombre de la propiedad. Este prefijo es parte integral del nombre de la propiedad y debe usarse siempre que se acceda a ella, incluso dentro de la clase. Los campos privados se declaran en el cuerpo de la clase (antes o dentro del constructor) y pueden tener valores iniciales.

sintaxis-campos-privados.js
Loading code...

Este ejemplo muestra una clase Persona con un campo privado #saldo inicializado en 0. El campo solo es accesible dentro de la clase mediante los métodos depositar, retirar y getSaldo. Intentar acceder a #saldo desde fuera de la clase (como persona.#saldo) genera un SyntaxError inmediato, no un error en tiempo de ejecución. Esto hace imposible acceder o modificar el saldo directamente.

Prefijo # obligatorio

El prefijo # es parte del nombre de la propiedad, no un operador ni decorador. Debes usarlo siempre que accedas a la propiedad, incluso dentro de la clase. Si olvidas el prefijo, JavaScript buscará una propiedad pública diferente con ese nombre.

Acceso a Campos Privados

Los campos privados solo son accesibles dentro de la clase donde se definen. No puedes acceder a ellos desde fuera de la clase, ni siquiera desde subclases que hereden de ella. Esto proporciona verdadera encapsulación y protege la implementación interna. La única forma de interactuar con campos privados desde fuera es mediante métodos públicos de la clase.

acceso-campos-privados.js
Loading code...

Este ejemplo intenta acceder al campo privado #saldo desde fuera de la clase. Esto genera un SyntaxError porque los campos privados no son accesibles desde fuera de la clase donde se definen. El error ocurre en tiempo de análisis (parsing), no en tiempo de ejecución, lo que significa que el código ni siquiera se ejecutará si contiene un intento de acceso a campo privado desde fuera.

SyntaxError, no ReferenceError

Intentar acceder a un campo privado desde fuera de la clase genera un SyntaxError, no un ReferenceError. Esto indica que es un error de sintaxis detectado al parsear el código, no un error de una variable no definida que se detecta en ejecución.

Métodos Privados

Además de campos privados, también puedes definir métodos privados usando el prefijo #. Los métodos privados solo son accesibles dentro de la clase donde se definen y no pueden ser invocados desde fuera. Esto te permite encapsular lógica auxiliar, validaciones internas, o cualquier operación que no deba formar parte de la API pública de la clase.

metodos-privados.js
Loading code...

Este ejemplo define un método privado #validarPassword que verifica si la contraseña es correcta. El método solo es accesible dentro de la clase a través del método público autenticar. Intentar invocar usuario.#validarPassword() desde fuera genera un SyntaxError. Esto protege la lógica de validación y permite cambiarla internamente sin afectar el código que usa la clase.

Métodos privados para lógica interna

Los métodos privados son útiles para encapsular lógica que no debe ser accesible desde fuera de la clase. Puedes usarlos para validaciones, cálculos internos, formateo de datos, o cualquier operación auxiliar que deba permanecer como detalle de implementación.

Getters y Setters con Campos Privados

Un patrón muy común es combinar campos privados con getters y setters para proporcionar acceso controlado. Los campos privados almacenan los valores reales, mientras que los getters y setters públicos proporcionan la interfaz para leerlos y modificarlos con validación. Este patrón es superior al uso de propiedades con prefijo _ porque garantiza que nadie pueda saltarse la validación accediendo directamente al campo.

getters-setters-privados.js
Loading code...

Este ejemplo muestra una clase Usuario con campos privados #email y #edad. Los getters y setters públicos proporcionan acceso controlado con validación. El setter de email verifica que contenga arroba, y el setter de edad verifica que esté en rango válido. Como los campos son verdaderamente privados, es imposible saltarse estas validaciones accediendo directamente a los campos, garantizando que el objeto siempre esté en un estado válido.

Validación garantizada

Al combinar campos privados con getters y setters, garantizas que todas las modificaciones pasen por tu validación. A diferencia del prefijo _, no hay forma de saltarse las validaciones accediendo directamente al campo.

Campos Estáticos Privados

JavaScript también permite definir campos y métodos estáticos privados usando static # en la definición. Los campos estáticos privados pertenecen a la clase misma, no a las instancias, y solo son accesibles desde métodos estáticos de la clase. Son útiles para mantener estado compartido entre todas las instancias de forma privada, como contadores, registros, o configuraciones internas.

campos-estaticos-privados.js
Loading code...

Este ejemplo muestra una clase Contador con un campo estático privado #instancias que cuenta cuántos objetos se han creado. El método estático privado #incrementarInstancias se llama desde el constructor para incrementar el contador. El método estático público obtenerTotalInstancias proporciona acceso de solo lectura al contador. Ni el campo estático #instancias ni el método #incrementarInstancias son accesibles desde fuera de la clase.

Estado compartido privado

Los campos estáticos privados son ideales para mantener estado compartido entre todas las instancias de una clase sin exponerlo públicamente. Puedes usarlos para patrones singleton, contadores, cachés internas, o configuraciones globales de la clase.

# vs _: Privacidad Real vs Convención

Es importante entender la diferencia entre el prefijo _ (convención) y el prefijo # (privacidad real). El prefijo _ es solo una convención que indica que una propiedad debería tratarse como privada, pero no hay ninguna protección real: cualquiera puede acceder y modificar propiedades con _ desde fuera de la clase. El prefijo # proporciona privacidad real a nivel de lenguaje, haciendo físicamente imposible acceder a estos campos desde fuera.

privado-vs-convencion.js
Loading code...

Este ejemplo demuestra la diferencia crucial entre ambos enfoques. Con prefijo _, la propiedad _privado es completamente accesible y modificable desde fuera de la clase, aparece en Object.keys(), y puede ser "hackeada" fácilmente. Con prefijo #, el campo #privado es verdaderamente inaccesible desde fuera, no aparece en Object.keys(), y cualquier intento de acceso genera un SyntaxError. La convención _ depende de disciplina del desarrollador, mientras que # es garantizado por el lenguaje.

  • <strong>Prefijo _</strong>: Solo convención, no hay protección real. La propiedad es accesible y modificable.
  • <strong>Prefijo #</strong>: Privacidad real garantizada por el lenguaje. Imposible acceder desde fuera.
  • <strong>_</strong>: Aparece en Object.keys() y puede ser enumerada.
  • <strong>#</strong>: No aparece en Object.keys() ni puede ser enumerada o accedida mediante reflexión.
  • <strong>_</strong>: Útil para compatibilidad con código antiguo o cuando necesitas acceso desde pruebas.
  • <strong>#</strong>: Usa siempre que necesites verdadera encapsulación y protección de datos.

Prefiere # sobre _ en código nuevo

En código nuevo, siempre prefiere usar campos privados con # sobre la convención _. Proporciona verdadera encapsulación, protege contra errores accidentales y maliciosos, y comunica claramente la intención de privacidad a nivel de lenguaje.

Verificar Campos Privados con in

JavaScript proporciona una forma de verificar si un objeto tiene un campo privado específico usando el operador in dentro de la clase. Esta característica es útil para verificar el tipo de un objeto basándose en la presencia de campos privados, implementar verificaciones de tipo más robustas, o distinguir entre instancias de diferentes clases.

verificar-campo-privado.js
Loading code...

Este ejemplo muestra cómo usar el operador in con campos privados. Dentro de la clase, puedes usar #campoPrivado in obj para verificar si un objeto tiene ese campo privado. Esto es útil para implementar métodos estáticos que verifican si un objeto es una instancia de la clase (patrón brand check), o para métodos que aceptan objetos y necesitan verificar si tienen los campos privados esperados antes de operar con ellos.

Brand checking con campos privados

El operador in con campos privados permite implementar brand checking robusto: verificar que un objeto es realmente una instancia de tu clase y no un objeto que simplemente tiene propiedades con los mismos nombres. Esto es más seguro que instanceof en ciertos casos.

Encapsulación y Mejores Prácticas

La encapsulación es el principio de ocultar la implementación interna de una clase y exponer solo una interfaz pública controlada. Los campos privados proporcionan verdadera encapsulación en JavaScript, permitiéndote proteger datos y lógica interna, mantener invariantes, y cambiar la implementación sin romper código existente que usa tu clase.

encapsulacion.js
Loading code...

Este ejemplo muestra una clase CuentaBancaria con campos privados #saldo y #pin que encapsulan datos sensibles. Los métodos públicos depositar, retirar y consultarSaldo proporcionan una interfaz controlada con validación. El saldo nunca puede volverse negativo y las operaciones requieren PIN correcto. Como los campos son privados, es imposible saltarse estas validaciones manipulando directamente #saldo o #pin.

Mejores prácticas

Al usar campos privados, sigue estas mejores prácticas: usa campos privados para cualquier dato que no deba ser parte de la API pública, combínalos con getters y setters para acceso controlado, documenta la API pública claramente, mantén los métodos privados pequeños y enfocados, y prefiere campos privados sobre la convención _ en código nuevo para garantizar verdadera encapsulación.

Interfaz pública controlada

Los métodos públicos actúan como una interfaz controlada para acceder a los campos privados. Esto te permite validar datos, mantener invariantes, ejecutar lógica adicional, y cambiar la implementación interna sin afectar código que usa la clase.

Resumen: Campos Privados

Conceptos principales:

  • Los campos privados usan prefijo # y son verdaderamente privados
  • Solo son accesibles dentro de la clase, ni siquiera en subclases
  • Puedes crear métodos privados, getters/setters, y campos estáticos privados
  • El prefijo # es diferente de _ (real vs convención)
  • Acceder desde fuera genera SyntaxError, no ReferenceError
  • Usa 'in' dentro de la clase para verificar existencia de campo privado

Mejores prácticas:

  • Prefiere # sobre _ en código nuevo para verdadera encapsulación
  • Combina campos privados con getters/setters para acceso controlado
  • Usa campos privados para datos sensibles y detalles de implementación
  • Usa métodos privados para encapsular lógica interna
  • Documenta claramente la API pública de la clase
  • Valida datos en setters que acceden a campos privados