Propiedades privadas y estáticas en clases JavaScript | Nicolás Garzón
Los miembros de una clase pueden pertenecer a cada instancia , pertenecer a la clase mediante static o ser privados mediante #.
JavaScript
Copiar class Account {
static accountCount = 0 ;
#balance = 0 ;
constructor ( owner ) {
this . owner = owner;
Account. accountCount++ ;
}
deposit ( amount ) {
this . #balance += amount;
}
}
owner pertenece a cada instancia.
#balance pertenece a cada instancia y es privado.
accountCount pertenece a la clase.
JavaScript
Copiar class User {
role = "viewer" ;
constructor ( name ) {
this . name = name;
}
} Tanto role como name son propiedades propias de cada instancia.
JavaScript
Copiar const first = new User ( "Nicolás" ) ;
const second = new User ( "Ana" ) ;
Object. hasOwn ( first, "role" ) ;
first. role === second. role; Si el valor inicial es un objeto o array, se evalúa para cada instancia.
JavaScript
Copiar class Cart {
items = [ ] ;
}
const firstCart = new Cart ( ) ;
const secondCart = new Cart ( ) ;
firstCart. items === secondCart. items; JavaScript
Copiar class Account {
#balance = 0 ;
readBalance ( ) {
return this . #balance;
}
} El nombre privado debe declararse dentro de la clase.
JavaScript
Copiar account. #balance; No puede leerse mediante corchetes.
JavaScript
Copiar account[ "#balance" ] ; Los campos privados no son propiedades normales con un nombre string oculto. Utilizan un mecanismo privado del lenguaje.
Antes de los campos #, era común utilizar un guion bajo.
JavaScript
Copiar class Account {
_balance = 0 ;
} _balance comunica “uso interno”, pero sigue siendo públicamente accesible.
JavaScript
Copiar account. _balance = 999999 ; Es una convención, no privacidad.
Con #balance, el acceso externo ni siquiera es sintácticamente válido.
JavaScript
Copiar class Account {
#balance = 0 ;
deposit ( amount ) {
this . #validateAmount ( amount) ;
this . #balance += amount;
}
#validateAmount ( amount ) {
if ( ! Number. isFinite ( amount) || amount <= 0 ) {
throw new TypeError (
"Amount must be a positive number" ,
) ;
}
}
} El método privado puede utilizarse desde cualquier método definido en el cuerpo de la misma clase.
JavaScript
Copiar class Temperature {
#celsius = 0 ;
get #fahrenheit ( ) {
return this . #celsius * 1.8 + 32 ;
}
readFahrenheit ( ) {
return this . #fahrenheit;
}
} Son menos comunes, pero siguen las mismas reglas de privacidad.
Un campo privado identifica qué objetos fueron inicializados por esa clase.
JavaScript
Copiar class Account {
#balance = 0 ;
static isAccount ( value ) {
return #balance in value;
}
} JavaScript
Copiar Account. isAccount ( new Account ( ) ) ;
Account. isAccount ( { } ) ; La expresión #balance in value solo puede escribirse dentro del cuerpo donde el nombre privado está declarado.
No ejecuta un getter público ni depende de propiedades con nombres parecidos.
JavaScript
Copiar class Account {
#balance = 0 ;
readBalance ( ) {
return this . #balance;
}
}
const readBalance = Account . prototype. readBalance;
readBalance . call ( { } ) ; El objeto normal no tiene la marca privada de Account.
Esto evita prestar arbitrariamente métodos que utilizan campos privados.
JavaScript
Copiar class Account {
#balance = 0 ;
readBalance ( ) {
return this . #balance;
}
}
class SavingsAccount extends Account { } Una instancia de SavingsAccount recibe la parte privada de Account, por lo que el método heredado funciona.
JavaScript
Copiar const account = new SavingsAccount ( ) ;
account. readBalance ( ) ; Pero la clase hija no puede escribir directamente this.#balance porque ese nombre privado pertenece al cuerpo de Account.
JavaScript
Copiar class SavingsAccount extends Account {
applyInterest ( ) {
return this . #balance;
}
} La clase base debe exponer métodos protegidos por contrato público si la hija necesita colaborar.
JavaScript no tiene un modificador nativo protected equivalente al de otros lenguajes.
JavaScript
Copiar class First {
#value = 1 ;
}
class Second {
#value = 2 ;
} Aunque ambos se escriban #value, son nombres privados distintos y no intercambiables.
JavaScript
Copiar class User {
static createGuest ( ) {
return new User ( "Guest" , "viewer" ) ;
}
constructor ( name, role ) {
this . name = name;
this . role = role;
}
} Se ejecutan sobre la clase.
JavaScript
Copiar const guest = User. createGuest ( ) ; No existen en la instancia.
JavaScript
Copiar guest. createGuest ( ) ;
Factories relacionadas con la clase.
Validaciones del tipo.
Conversión desde formatos externos.
Utilidades que conceptualmente pertenecen al modelo.
JavaScript
Copiar class Order {
static validStatuses = new Set ( [
"pending" ,
"completed" ,
"cancelled" ,
] ) ;
} Existe una sola propiedad en Order, no una copia por instancia.
JavaScript
Copiar Order. validStatuses;
new Order ( ) . validStatuses; Si el valor es mutable, se comparte globalmente entre todas las instancias y llamadas.
JavaScript
Copiar Order. validStatuses. add ( "refunded" ) ; Ese cambio es visible en toda la aplicación que usa la misma clase.
JavaScript
Copiar class User {
static type = "user" ;
static readType ( ) {
return this . type;
}
} JavaScript
Copiar User. readType ( ) ; Aquí this es la clase receptora.
Una clase hija puede heredar el método estático.
JavaScript
Copiar class Administrator extends User {
static type = "administrator" ;
}
Administrator. readType ( ) ; El método fue definido en User, pero se llamó sobre Administrator.
JavaScript
Copiar class TokenService {
static #secret = "internal-secret" ;
static sign ( payload ) {
return ` ${ payload} . ${ this . #secret} ` ;
}
} El campo pertenece a la clase y no puede accederse desde fuera.
Hay una sutileza con herencia:
JavaScript
Copiar class SpecializedTokenService extends TokenService { }
SpecializedTokenService. sign ( "data" ) ; Si sign usa this.#secret, this será la clase hija, que no posee la marca privada estática de la clase base, y puede producir un error.
Cuando el dato privado pertenece estrictamente a la clase base, puede ser más correcto referirse a ella directamente.
JavaScript
Copiar static sign ( payload ) {
return ` ${ payload} . ${ TokenService. #secret} ` ;
} Elige conscientemente entre comportamiento polimórfico mediante this y acceso fijo a la clase base.
JavaScript
Copiar class Configuration {
static values;
static {
const environment =
globalThis. process?. env?. NODE_ENV ??
"development" ;
this . values = {
environment,
debug : environment !== "production" ,
} ;
}
} Un bloque estático se ejecuta una vez cuando se evalúa la clase.
Acceder a miembros privados estáticos.
Ejecutar varias sentencias.
Preparar estado de clase.
Manejar errores de inicialización.
No debe utilizarse para esconder trabajo costoso o efectos inesperados durante la importación de un módulo.
En una clase base, los campos de instancia se inicializan antes del cuerpo del constructor.
JavaScript
Copiar class Example {
value = "field" ;
constructor ( ) {
console. log ( this . value) ;
}
} En una clase derivada, los campos de la hija se inicializan después de que super() retorna y antes de continuar con las siguientes instrucciones del constructor derivado.
Este orden importa cuando existen setters, sobrescrituras o llamadas a métodos durante la construcción. Evita depender de comportamiento complejo entre campos y constructores.
Un campo privado limita el acceso desde código JavaScript externo normal, pero no:
Cifra el dato.
Protege la memoria del proceso.
Evita que un método público lo exponga.
Convierte secretos del frontend en secretos reales.
Sustituye controles de autorización del backend.
JavaScript
Copiar class Session {
#token;
readToken ( ) {
return this . #token;
}
} El método público continúa exponiendo el token.
JavaScript
Copiar JSON . stringify ( instance) ; Los campos privados no se incluyen.
JavaScript
Copiar Object. keys ( instance) ;
Reflect. ownKeys ( instance) ; Una copia mediante spread solo incluye propiedades públicas enumerables.
JavaScript
Copiar const data = {
... instance,
} ; Para persistencia o transporte, define una representación pública explícita.
JavaScript
Copiar class Account {
toJSON ( ) {
return {
owner : this . owner,
balance : this . #balance,
} ;
}
} Decide cuidadosamente qué información debe salir.
JavaScript
Copiar class Connection {
static #activeCount = 0 ;
#isOpen = false ;
open ( ) {
if ( this . #isOpen) {
return ;
}
this . #isOpen = true ;
Connection. #activeCount++ ;
}
close ( ) {
if ( ! this . #isOpen) {
return ;
}
this . #isOpen = false ;
Connection. #activeCount-- ;
}
static readActiveCount ( ) {
return Connection. #activeCount;
}
} Cada instancia conserva su estado privado. La clase mantiene un contador privado compartido.
Pensar que _property es realmente privada.
Intentar acceder a #field mediante corchetes.
Creer que una clase hija puede usar directamente los privados de la base.
Prestar un método que requiere la marca privada a un objeto cualquiera.
Colocar estado mutable en un campo estático sin reconocer que se comparte.
Esperar que un miembro estático exista en las instancias.
Usar this.#privateStatic en herencia sin entender la marca privada.
Ocultar red o trabajo costoso dentro de un bloque estático.
Confundir privacidad del lenguaje con cifrado o autorización.
Esperar que JSON incluya campos privados.
Los campos públicos pertenecen a cada instancia.
# crea privacidad real del lenguaje.
Los nombres privados solo pueden usarse dentro de la clase que los declara.
Una hija no puede acceder directamente a privados de la base.
#field in object permite comprobar la marca privada dentro de la clase.
Los miembros static pertenecen a la clase.
Los métodos estáticos pueden heredarse y reciben la clase receptora como this.
El estado estático mutable se comparte.
Los bloques estáticos se ejecutan una vez al definir la clase.
Privacidad no sustituye seguridad, autorización ni cifrado.
JavaScript
Copiar class Account {
#balance = 0 ;
read ( ) {
return this . #balance;
}
}
const read = Account . prototype. read;
read . call ( { } ) ; Respuesta El objeto {} no fue inicializado como una instancia de Account y no contiene la marca privada #balance. El acceso produce un TypeError.
Herencia y composición explica cómo extender comportamiento, sobrescribir métodos y decidir cuándo una relación debe modelarse mediante jerarquía o colaboración.