Aprende leyendo en orden

El constructor y los campos de clase — cómo fijar los valores iniciales

Cómo fijan el constructor y los campos de clase los valores iniciales de una instancia: guardar argumentos, el orden de los campos, valores por defecto y rechazo de valores inválidos.

Un ticket de soporte tiene valores que cambian de un ticket a otro, como el título, y valores iniciales que son iguales en todos, como el estado «Pendiente». Si escribes la clase sin cuidado, los valores omitidos o inválidos terminan guardados en la instancia.

En este artículo verás el constructor, que recibe los valores que se pasan al crear la instancia, y los campos de clase, que declaran valores iniciales fijos.

Limpiar los argumentos antes de guardarlos — el cuerpo del constructor

Los títulos que llegan desde el formulario de soporte a veces traen espacios de más al principio o al final. Si armas un texto para mostrar como «Ana: No puedo iniciar sesión» después de crear la instancia, cada pantalla que lo muestre necesita su propio trim y su propio código para armar la cadena.

En el cuerpo del constructor no tienes que guardar el argumento sin cambios. Puedes guardar un valor ya limpio, como en this.title = title.trim(), o un valor formado a partir de varios argumentos. Así, cada pantalla solo lee las propiedades de la instancia y obtiene valores que ya están limpios.

class Ticket {
  constructor(title, customerName) {
    // Limpia el argumento antes de guardarlo en this
    this.title = title.trim();
    this.customerName = customerName;

    // Arma el valor para mostrar a partir de this, después de la limpieza
    this.label = `${this.customerName}: ${this.title}`;
    // Si escribieras `${customerName}: ${title}`, quedarían los espacios del título
  }
}

// Pasa un título con espacios antes y después
const ticket = new Ticket("  No puedo iniciar sesión ", "Ana");
console.log(ticket.title);    // No puedo iniciar sesión
console.log(ticket.label);    // Ana: No puedo iniciar sesión
De qué valor se arma label
Se arma con elargumento titletitle aún tieneespaciosIncluye tambiénlos espaciosEspacios de mástras "Ana:"Se arma conthis.titlethis.title yapasó por trimIncluye eltítulo limpioAna: No puedoiniciar sesión
Ninguna de las dos filas lanza un error; la diferencia solo se ve en lo que se muestra. Para usar el valor limpio, léelo de this después de guardarlo.

La fila de arriba lee el argumento title, y la de abajo lee this.title después del trim. Si mueves la línea de label al principio del constructor, todavía no se ha asignado ni this.title ni this.customerName, así que label queda como undefined: undefined.

Crea datos de reserva a partir de los valores de un formulario. selectedRoom, selectedStart y selectedHours ya están declaradas.

① Define Reservation para que cada instancia guarde el nombre de la sala, la hora de inicio y la duración.

② En el constructor, guarda también la hora de fin como endHour.

③ Pasa los tres valores para crear booking.

④ Muéstralo con el formato «Sala: N:00-N:00 (N horas)».

(Si se ejecuta correctamente, aparecerá una explicación).

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Declarar valores iniciales fijos — campos de clase

Todo ticket nuevo empieza con el estado «Pendiente» y cero comentarios. Si fijas estos valores iniciales, que no dependen de los argumentos, dentro del constructor con this.status = "Pendiente";, se mezclan con las líneas que guardan los argumentos, y para saber qué contiene una instancia tienes que leer todo el cuerpo.

Un campo de clase (una propiedad que se declara dentro de las llaves de la clase pero fuera del constructor y de los métodos, y que se agrega a la instancia en cada new) es solo un nombre y un valor inicial, sin this, como status = "Pendiente";. Para leerlo desde el cuerpo del constructor o desde un método, escribes this.status, igual que con cualquier otra propiedad.

class Ticket {
  // Los valores iniciales que no dependen de los argumentos van en campos
  status = "Pendiente";
  comments = [];

  constructor(title) {
    this.title = title;
    this.summary = `[${this.status}] ${title}`;   // Aquí ya se pueden leer los campos
  }
}

const first = new Ticket("No puedo iniciar sesión");
const second = new Ticket("Factura no recibida");
console.log(first.summary);                // [Pendiente] No puedo iniciar sesión

// comments es un array distinto, creado en cada new
first.comments.push("Se pidió al cliente reiniciar");
console.log(first.comments.length);        // 1
console.log(second.comments.length);       // 0
El orden de los pasos en new Ticket
new Ticket("Nopuedo iniciarsesión")① Agrega camposde arriba abajo② Ejecuta cuerpo,lee this.status③ Asigna lainstancia a firstLa instanciaestá vacíastatus:"Pendiente"comments: []Se agregantitle y summaryYa tiene4 propiedades
Dentro de new, primero se agregan los campos y después se ejecuta el cuerpo del constructor. El cuerpo ya puede leer los valores de los campos.

El paso ① se repite en cada new, así que comments = [] es un array distinto para cada instancia, y un comentario agregado a first no aparece en second. Por eso second.comments.length sigue en 0.

Escribir this. en un campo es un SyntaxError

Si escribes this.status = "Pendiente"; donde van los campos (fuera del constructor), obtienes SyntaxError: Unexpected token '.' y no se ejecuta ni una línea. Agregar let o const también es un SyntaxError. Un campo es solo un nombre y un valor inicial.

Crea datos de envío para cada destino. destinations ya está declarada.

① Define Shipment, que recibe un destino y empieza con un costo de envío de 600 y el estado «Recibido».

② En el constructor, cambia el costo a 1500 solo cuando el destino sea «Okinawa».

③ Crea un Shipment para cada destino y ponlos en el array shipments.

④ Muestra cada uno con el formato «Destino: estado / Envío N yenes».

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

El valor cuando se omite un argumento — parámetros por defecto

La mayoría de los tickets tiene prioridad «Normal», y quieres pasar «Urgente» solo cuando algo corre prisa. Si escribes el campo priority = "Normal"; y además agregas this.priority = priority; al cuerpo, un ticket creado sin prioridad no termina con «Normal».

Los parámetros por defecto (la sintaxis que fija el valor que se usa cuando no se pasa un argumento), que viste en el artículo sobre parámetros por defecto y parámetros rest, también funcionan en un constructor: escribe priority = "Normal" en la lista de parámetros. Un argumento omitido o undefined se reemplaza por el valor por defecto antes de que se ejecute el cuerpo, así que this.priority = priority recibe «Normal».

// Un valor por defecto en el campo y, además, asignar el argumento en el cuerpo
class DraftTicket {
  priority = "Normal";
  constructor(title, priority) {
    this.title = title;
    this.priority = priority;   // El priority de la derecha es el argumento (undefined si se omite)
  }
}
console.log(new DraftTicket("No puedo iniciar sesión").priority);            // undefined

// Si un argumento puede cambiar el valor, pon el valor por defecto en un parámetro por defecto
class Ticket {
  constructor(title, priority = "Normal") {
    this.title = title;
    this.priority = priority;
  }
}
console.log(new Ticket("No puedo iniciar sesión").priority);                 // Normal
console.log(new Ticket("Factura no recibida", "Urgente").priority);         // Urgente
Cómo se pierde el «Normal» del campo
new DraftTicket("No puedo iniciarsesión")① Agrega loscampos② El cuerpoasigna this.title③ El cuerpo asignathis.priorityEl argumentopriority esundefinedthis.priorityes "Normal"this.prioritysigue en "Normal"this.priorityes undefined
El «Normal» del campo se asigna antes del cuerpo, y luego la asignación del cuerpo lo sobrescribe con el undefined del argumento. Al final queda el valor asignado en el cuerpo.

Los valores que un argumento puede cambiar van en parámetros por defecto, y los que no dependen de los argumentos, en campos. Para un valor que depende de una condición sobre los argumentos, como el costo de envío del Ejercicio 2, usa un if en el cuerpo para cambiar el valor inicial del campo. La tabla de abajo muestra qué queda en priority con cada forma, con y sin «Urgente», incluida una versión que nunca lo asigna en el cuerpo.

FormaSolo el títuloCon "Urgente"
Solo campo (sin asignar en cuerpo)NormalNormal (se ignora)
Campo + this.priority = priorityundefinedUrgente
Por defecto: priority = "Normal"NormalUrgente

Rechazar valores inválidos — throw en el constructor

Si terminas con un ticket que tiene el título vacío, o una prioridad que no es uno de los valores acordados, como «Alta», la lista muestra una fila en blanco y el ticket queda fuera cuando cuentas los «Urgente». Aunque escribas una función aparte para revisar los tickets después de crearlos, cualquier pantalla que se olvide de llamarla sigue usando la instancia inválida.

Para la validación al crear (comprobar, en el momento de crear una instancia, que los valores recibidos cumplen las reglas), escribe un if dentro del constructor y lanza una excepción con throw new Error(...) cuando un valor no cumpla. Una vez lanzada la excepción, la expresión new no devuelve ninguna instancia.

class Ticket {
  constructor(title, priority = "Normal") {
    // Rechaza los valores inválidos con una excepción antes de que lleguen a this
    if (title.trim() === "") {
      throw new Error("El título está vacío");
    }
    if (priority !== "Normal" && priority !== "Urgente") {
      throw new Error(`La prioridad debe ser Normal o Urgente: ${priority}`);
    }
    this.title = title.trim();
    this.priority = priority;
  }
}

try {
  const ticket = new Ticket("No puedo iniciar sesión", "Alta");
  console.log(ticket.title);    // Esta línea nunca se ejecuta
} catch (error) {
  console.log(error.message);   // La prioridad debe ser Normal o Urgente: Alta
}
Adónde va un throw dentro de new
Dentro de try (aquí se llama a new)
  • const ticket = new Ticket(...): el lado derecho lanza la excepción y ticket no recibe nada
  • console.log(ticket.title): nunca se ejecuta
Cuerpo del constructor llamado
  • Revisión del título: no está vacío, así que pasa
  • Revisión de la prioridad: es "Alta", así que ejecuta throw
  • this.title = ...: de aquí para abajo no se ejecuta nada
Dentro de catch (error)
  • error.message: «La prioridad debe ser Normal o Urgente: Alta»
La excepción sale de la expresión new, se salta el resto del try y llega a catch. La instancia inválida nunca queda en ninguna variable.

Con la validación en el constructor, ningún ticket puede saltársela, sin importar en cuántos lugares se llame a new Ticket(...). Si incluyes el valor rechazado en el mensaje de la excepción, el lado del catch también puede mostrar qué valor era inválido.

Las asignaciones después de crear la instancia no se validan

La validación del constructor solo se ejecuta durante new. Si asignas ticket.priority = "Alta"; después de crear el ticket, no se produce ningún error y el valor simplemente se sobrescribe. Cómo validar el valor en cada asignación lo verás en el artículo sobre getters y setters.

Acepta solo las líneas de pedido que cumplen las reglas. requests y acceptedLines ya están declaradas.

① Define OrderLine, que recibe un nombre de producto y una cantidad (1 si no se indica cantidad).

② Lanza una excepción con «El nombre del producto está vacío» si el nombre está vacío, o con «Cantidad no válida: N» si la cantidad no es un entero positivo.

③ Convierte cada solicitud en un OrderLine. Si se crea, agrégalo a acceptedLines y muestra «Aceptado: nombre x cantidad»; si no, muestra «Rechazado: mensaje de la excepción».

④ Muestra cuántas líneas se aceptaron con el formato «Líneas aceptadas: N».

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado
QUIZ

Verificación de conocimientos

Responde cada pregunta una a una.

Pregunta 1En el constructor de una clase que tiene status = "Pendiente";, ¿qué obtienes al leer this.status?

Pregunta 2Una clase tiene solo el campo priority = "Normal"; y no lo asigna en el cuerpo. ¿Qué pasa si pasas «Urgente» al crear la instancia?

Pregunta 3Dentro de un try, el constructor lanza una excepción durante const ticket = new Ticket(""). ¿Qué pasa?