Pregunta 1Parcel tiene static count = 0;. ¿Qué obtienes al leer first.count en una instancia first?
Miembros de instancia y static — valores y métodos de la propia clase
Cómo static pone valores y métodos en la propia clase: contadores con campos static, métodos de fábrica que crean instancias desde JSON, constantes compartidas y cuándo usar miembros de instancia.
Para numerar paquetes 1, 2, 3 en el orden en que los creas, tienes que llevar la cuenta de cuántos llevas creados. El destinatario cambia de un paquete a otro, pero de esa cuenta y del límite de peso basta con una sola copia, compartida por todos los paquetes.
En este artículo verás static, que pone valores y métodos en la propia clase en lugar de en sus instancias.
Contar paquetes — campos static
Quieres numerar los paquetes en el orden en que se crean. Si escribes el campo de clase count = 0;, lo aumentas con this.count += 1 en el constructor y lo usas como número, todos los paquetes reciben el número 1, porque el campo vuelve a empezar en 0 en cada new.
Un campo static (una propiedad que se declara dentro de la clase con la palabra clave static y de la que existe una sola copia, en la propia clase) se lee y se escribe a través del nombre de la clase, como Parcel.count.
En cambio, las propiedades que tiene cada instancia, como this.number, y los métodos escritos sin static se llaman miembros de instancia.
class Parcel {
// Cantidad de paquetes creados hasta ahora, guardada una sola vez en la clase
static count = 0;
constructor(recipient) {
Parcel.count += 1; // Aumenta en uno el count de la clase
this.number = Parcel.count; // Usa la cuenta de este momento como número del paquete
this.recipient = recipient;
}
}
// Crea dos paquetes
const first = new Parcel("Ana");
const second = new Parcel("Carlos");
console.log(first.number, second.number); // 1 2
console.log(Parcel.count); // 2
console.log(first.count); // undefined
static count: 1 después del primernew, 2 después del segundo- Se lee y se escribe como
Parcel.count
number: 1 (la cuenta al crearla)recipient: «Ana»- No hay
count:first.countesundefined
number: 2 (la cuenta al crearla)recipient: «Carlos»
count, en el recuadro de la clase, y ninguno en los de las instancias. Si lo lees desde una instancia, obtienes undefined.Cada new aumenta en uno el mismo count del recuadro de la clase y copia su valor actual en el number propio de la nueva instancia. Aunque después count suba a 2, first.number se queda en 1, el valor que recibió al crearse.
Crear paquetes desde JSON — métodos static
Cuando cargas un paquete guardado, recibes un texto JSON con el destinatario y el peso. Si cada pantalla que carga paquetes tiene su propio JSON.parse y su propio new Parcel(...), un cambio en los nombres de las claves del JSON te obliga a corregir todas esas pantallas.
Pero cuando creas un paquete a partir de JSON, todavía no hay ninguna instancia sobre la que llamar a un método. Un método static (un método definido con static que se llama a través del nombre de la clase, sin crear una instancia) se puede llamar como Parcel.fromJSON(text). Un método static como fromJSON, que crea y devuelve una instancia, se llama método de fábrica.
class Parcel {
constructor(recipient, weightKg) {
this.recipient = recipient;
this.weightKg = weightKg;
}
label() {
return `${this.recipient} (${this.weightKg} kg)`;
}
// Recibe un texto JSON, crea una instancia de Parcel y la devuelve
static fromJSON(text) {
const data = JSON.parse(text);
return new Parcel(data.recipient, data.weightKg);
}
}
const text = '{"recipient":"Ana","weightKg":2}';
const parcel = Parcel.fromJSON(text); // Se llama a través del nombre de la clase
console.log(parcel.label()); // Ana (2 kg)
JSON.parse te da un objeto simple con propiedades, pero sin métodos. Solo después de pasar por new se convierte en una instancia que puede llamar a label().La última línea de fromJSON es new Parcel(...), así que el cuerpo del constructor se ejecuta como siempre. Si pones validaciones con throw en el constructor, los paquetes que cargas desde JSON pasan por las mismas validaciones que los que se ingresan en pantalla.
No puedes llamar a fromJSON sobre una instancia
Si lo llamas sobre una instancia, como en parcel.fromJSON(text), se lanza TypeError: parcel.fromJSON is not a function. A diferencia de label(), un método static solo se puede llamar a través del nombre de la clase. Cómo funciona esto lo verás en el artículo sobre prototipos.
Un límite fijo para todos los paquetes — constantes static
El límite de peso de 30 kg se usa tanto en el aviso del formulario de ingreso, que se muestra antes de que exista ningún paquete, como en el cálculo de la capacidad restante de cada paquete. Si pones el límite en un campo de instancia, tendrías que crear un paquete sin destinatario ni peso solo para mostrar el aviso en el formulario.
Una constante static (un campo static con un valor que no cambia mientras se ejecuta el programa) se nombra todo en mayúsculas, con las palabras separadas por guiones bajos, como en static MAX_WEIGHT_KG = 30;. Tanto fuera de la clase como dentro de un método, se lee a través del nombre de la clase: Parcel.MAX_WEIGHT_KG.
class Parcel {
// El límite común a todos los paquetes se guarda una sola vez en la clase con static
static MAX_WEIGHT_KG = 30;
constructor(weightKg) {
this.weightKg = weightKg;
}
// Cuántos kg más caben antes del límite
remainingKg() {
return Parcel.MAX_WEIGHT_KG - this.weightKg;
// Si escribes this.MAX_WEIGHT_KG - this.weightKg, devuelve NaN
}
}
// Se puede leer incluso antes de que exista una instancia
console.log(`Hasta ${Parcel.MAX_WEIGHT_KG} kg por paquete`); // Hasta 30 kg por paquete
const parcel = new Parcel(12);
console.log(parcel.remainingKg()); // 18
La columna derecha termina en NaN porque this dentro de un método es la instancia, y las constantes static no están en la instancia. undefined - 12 no lanza ningún error, así que solo te das cuenta cuando el peso restante que se muestra no tiene sentido.
Aunque esté en mayúsculas, se puede reasignar
Un nombre en mayúsculas es solo una convención que significa «no cambies esto»; si asignas Parcel.MAX_WEIGHT_KG = 50;, el valor cambia. Si defines solo un getter, como en static get MAX_WEIGHT_KG() { return 30; }, asignarle un valor lanza un TypeError (los getters se ven en el siguiente artículo).
Decidir dónde vive un valor — static frente a instancia
Cuando conoces static, puede parecer práctico poner en la clase un valor como el destinatario para leerlo desde un solo lugar, Parcel.recipient. En una prueba rápida que crea un solo paquete, se muestra el mismo destinatario tanto si es static como si está en this, así que no notarás la diferencia.
Decide dónde vive un valor según si cambia de un elemento a otro. Un valor puesto en this existe por separado en cada instancia que crea new, mientras que un valor static existe una sola vez en la clase, crees los paquetes que crees.
// Destinatario guardado en static
class SharedParcel {
static recipient = "";
constructor(recipient) { SharedParcel.recipient = recipient; }
label() { return `Para: ${SharedParcel.recipient}`; }
}
// Destinatario guardado en la instancia
class Parcel {
constructor(recipient) { this.recipient = recipient; }
label() { return `Para: ${this.recipient}`; }
}
const sharedFirst = new SharedParcel("Ana");
new SharedParcel("Carlos");
console.log(sharedFirst.label()); // Para: Carlos
const first = new Parcel("Ana");
new Parcel("Carlos");
console.log(first.label()); // Para: Ana
sharedFirst no tiene destinatario propio, y label() lee SharedParcel.recipient tal como está en el momento de la llamada, así que muestra «Carlos», el último valor asignado. La tabla de abajo resume los miembros que usamos en las clases de paquetes de este artículo: cómo se escribe cada tipo y cómo se usa.
| Qué guarda | Cómo se escribe y se usa | Ejemplos del artículo |
|---|---|---|
| Un valor por elemento | En this, se lee de la instancia | number / recipient / weightKg |
| Método con valores propios | Sin static, se llama en la instancia | label() / remainingKg() |
| Un valor para todos | Con static, se lee por la clase | count / MAX_WEIGHT_KG |
| Lógica sin instancia | Con static, se llama por la clase | fromJSON(text) |
Verificación de conocimientos
Responde cada pregunta una a una.
Pregunta 2¿Qué pasa si llamas a un método static sobre la instancia parcel, como en parcel.fromJSON(text)?
Pregunta 3Una clase guarda el destinatario en un campo static. Si creas un paquete para «Ana» y después otro para «Carlos», ¿qué destinatario muestra el label() del primero?