Aprende leyendo en orden

Parámetros por defecto y rest — valores de respaldo y cómo recoger el resto

Aprende los parámetros por defecto, los parámetros rest que reúnen los argumentos en un array y el patrón de objeto options.

Hay argumentos que reciben el mismo valor en casi todas las llamadas, como un costo de envío o una tasa de impuesto. Si omites uno, no hay ningún error; solo te das cuenta de que falta cuando aparece undefined en lo que se muestra.

Este artículo trata los parámetros por defecto, que fijan el valor que se usa cuando se omite un argumento; los parámetros rest, que reúnen cualquier cantidad de argumentos, y el patrón options, que pasa la configuración como un solo objeto.

Hacer que un argumento sea opcional — parámetros por defecto

Supón que tienes una función que arma la reserva de una sala de reuniones, donde la duración casi siempre es de 1 hora y la disposición casi siempre es tipo aula. Si aun así tienes que escribir los tres argumentos en cada llamada, el único valor que de verdad quieres cambiar se pierde entre los demás.

Un parámetro por defecto (una forma de fijar de antemano el valor que se usa cuando no se pasa un argumento) se escribe agregando = en la lista de parámetros, como en hours = 1. Un parámetro con valor por defecto se puede omitir en la llamada.

// El valor por defecto se escribe agregando "= valor" en la lista de parámetros
const reserveRoom = (roomName, hours = 1, layout = "aula") => {
  return `${roomName} / ${hours} h / ${layout}`;
};

// Si omites el 3.º, se usa el valor por defecto "aula"
console.log(reserveRoom("Sala A", 2));               // Sala A / 2 h / aula

// Si pasas un valor, no se usa el valor por defecto
console.log(reserveRoom("Sala A", 2, "imperial"));   // Sala A / 2 h / imperial

// Si también omites el 2.º, se usan los dos valores por defecto
console.log(reserveRoom("Sala A"));                  // Sala A / 1 h / aula
Los argumentos se asignan desde la izquierda
Pasa Sala Aa roomNameSin valorpor defectoroomName esSala APasa 2a hoursEl 1 por defectono se usahours es 2No se pasanada a layoutAquí se aplica elvalor por defectolayout esaula
Aquí se sigue reserveRoom("Sala A", 2) posición por posición. Aunque haya un valor por defecto, no se aplica si se pasa un valor.

Solo puedes omitir argumentos empezando por la derecha, así que pon al final los parámetros con valor por defecto. Aunque solo quieras cambiar el 3.º argumento, igual tienes que escribir un valor en la 2.ª posición.

Calcula el costo de un envío. boxCount y la tarifa por caja de cada zona ya están declaradas, y el envío es la tarifa por caja × la cantidad de cajas.

① Escribe una función que reciba una cantidad de cajas y una zona, y devuelva el costo de envío, con 1 caja y Honshu (la isla principal de Japón) como valores por defecto.

② Omite la zona y muestra el envío de boxCount cajas como "Honshu: X yenes".

③ Indica Hokkaido como zona y muestra "Hokkaido: X yenes".

④ No pases ningún argumento y muestra "Por defecto: X yenes".

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

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Qué recibe un argumento que no pasas — undefined y valores por defecto

Si omites el argumento de un parámetro sin valor por defecto, cualquier cálculo o string que lo use sigue adelante igual. Como nada se detiene, después terminas buscando cuál fue la llamada que se olvidó de pasarlo.

Un parámetro al que no se le pasa ningún argumento recibe undefined. El valor por defecto se usa solo cuando el argumento es undefined: si pasas null, 0 o un string vacío, se usa el valor que pasaste.

const notifyStatus = (orderId, status = "Recibido") => `${orderId}: ${status}`;

// Omitirlo y pasar undefined dan los dos el valor por defecto
console.log(notifyStatus("A-1021"));             // A-1021: Recibido
console.log(notifyStatus("A-1021", undefined));  // A-1021: Recibido

// null cuenta como un valor pasado, así que no se usa el valor por defecto
console.log(notifyStatus("A-1021", null));       // A-1021: null

// Un parámetro omitido sin valor por defecto recibe undefined
const plainStatus = (orderId, status) => `${orderId}: ${status}`;
console.log(plainStatus("A-1021"));              // A-1021: undefined
Omitirlo frente a pasar null
Llamadasin statusstatus esundefinedSe aplica= "Recibido"A-1021:RecibidoPasa nulla statusstatus esnullNo se aplica elvalor por defectoA-1021:null
Un status omitido recibe undefined, así que se ejecuta la expresión del valor por defecto. null se trata como un valor pasado, así que el valor por defecto no se aplica.

La tabla de abajo muestra qué termina en status y qué se muestra, según el segundo argumento que le pases a notifyStatus. Pasar undefined da el mismo resultado que omitir el argumento, mientras que null, igual que un string, se muestra tal como lo pasaste.

LlamadaValor de statusSe muestra
notifyStatus("A-1021")El valor por defecto, RecibidoA-1021: Recibido
notifyStatus("A-1021", "Enviado")EnviadoA-1021: Enviado
notifyStatus("A-1021", undefined)El valor por defecto, RecibidoA-1021: Recibido
notifyStatus("A-1021", null)nullA-1021: null

Si pasas null, se salta el valor por defecto

null es un valor que significa "vacío a propósito", así que un valor por defecto en la lista de parámetros no lo reemplaza. Si quieres el valor de respaldo incluso cuando la respuesta de una API trae null, no pongas un valor por defecto en el parámetro y usa ?? dentro de la función, como en const shown = status ?? "Recibido";.

Muestra el stock de cada producto. products ya está declarado; incluye un producto con stock 0, uno sin la clave stock y uno con null.

① Escribe stockLabel, que recibe una cantidad en stock y devuelve "stock: X", con "por confirmar" como valor por defecto.

② Muestra todos los productos con el formato "[nombre del producto] stock: X".

③ Escribe stockLabelSafe, que también convierte null en "por confirmar", sin usar un valor por defecto.

④ Vuelve a mostrar el Cable y el Hub USB, esta vez con stockLabelSafe.

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Reunir cualquier cantidad de argumentos — parámetros rest

Hay funciones que reciben una cantidad distinta de valores en cada llamada, como las etiquetas de un producto o los conceptos de un informe de gastos. Aunque declares cinco parámetros, tarde o temprano alguna llamada va a necesitar un sexto, y los que no se usan quedan en undefined cada vez.

Un parámetro rest (una forma de recibir los argumentos restantes reunidos en un solo array; también se conocen como argumentos variables) se escribe como ...labels al final de la lista de parámetros. Es el mismo ... que reunía los elementos restantes en la desestructuración, solo que aquí reúne argumentos. Solo puede ser el último parámetro; si pones otro parámetro después, es un SyntaxError.

// Si escribes ...labels, los argumentos recibidos se reúnen en un solo array
const buildTags = (...labels) => `Etiquetas (${labels.length}): ${labels.join(" / ")}`;

console.log(buildTags("Envío gratis", "En stock"));  // Etiquetas (2): Envío gratis / En stock
console.log(buildTags("Nuevo"));                      // Etiquetas (1): Nuevo

// Al revés, ... dentro de los paréntesis de la llamada convierte un array de nuevo en argumentos
console.log(buildTags(...["Nuevo", "Limitado"]));     // Etiquetas (2): Nuevo / Limitado

// Si también hay parámetros con nombre, pon el parámetro rest al final
const buildNotice = (orderId, ...notes) => `${orderId} (${notes.join(", ")})`;

console.log(buildNotice("A-1021", "Refrigerado", "Con horario"));  // A-1021 (Refrigerado, Con horario)
console.log(buildNotice("A-1022"));                                // A-1022 ()
A qué parámetro va cada valor
1.º"A-1021"Va aorderId2.º"Refrigerado"Va anotes[0]3.º"Con horario"Va anotes[1]
El primer valor va a orderId, y los demás van, en orden, a los elementos de notes. Un parámetro rest recibe todo lo que queda después de los parámetros con nombre.

Un parámetro rest no puede tener valor por defecto, pero cuando no se le pasa ningún argumento, notes es un array vacío, no undefined. Al revés, si tienes un array y quieres pasar sus elementos como argumentos individuales, escribe ... dentro de los paréntesis de la llamada.

Suma los gastos de transporte de un viaje de trabajo. trainFare, busFare y taxiFare ya están declaradas.

① Escribe sumExpenses con un parámetro rest para que sume todos los montos que reciba y devuelva el total.

② Muestra el total de los 3 pasajes como "Total: X yenes".

③ Muestra, con el mismo formato, el resultado de llamarla sin argumentos.

④ Escribe summarize, que recibe el nombre de una categoría seguido de cualquier cantidad de montos; pásale "Transporte" y los 3 pasajes, y muestra "Transporte: X yenes (N gastos)".

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Pasar la configuración por nombre — objetos options y desestructuración

Cuando se acumulan opciones de configuración, como al enviar un correo, terminas con llamadas como sendMail(to, subject, false, true). No hay forma de saber qué significan ese false y ese true sin abrir la definición de la función.

Con el patrón options (una forma de pasar varias opciones de configuración reunidas en un solo objeto), la función que lo recibe desestructura el objeto y le da a cada clave un valor por defecto. Para las llamadas que no pasan ninguna configuración, agrega = {} después del patrón de desestructuración para que se use un objeto vacío cuando no llega ninguno.

// Cuantos más parámetros hay, más difícil es saber qué significa cada valor de la llamada
const sendMailOld = (to, subject, isUrgent, withPdf) =>
  `${to} / ${subject} / ${isUrgent} / ${withPdf}`;

console.log(sendMailOld("ana@example.com", "Aviso de envío", false, true));
// ana@example.com / Aviso de envío / false / true

// Patrón options: reúne la configuración en un objeto y desestructúralo al recibirlo
const sendMail = (to, { subject = "Aviso", isUrgent = false } = {}) =>
  `${to} / ${subject} / ${isUrgent}`;

console.log(sendMail("ana@example.com", { isUrgent: true }));  // ana@example.com / Aviso / true
console.log(sendMail("ana@example.com"));                     // ana@example.com / Aviso / false
Solo se reemplazan las claves que escribes
Pasa{ isUrgent: true }true llegaa isUrgentisUrgent estrueNo hay subject enla misma llamadaSe aplica el valorpor defectosubject esAvisoOtra llamada,sin 2.º argumento= {} poneun objeto vacíoLas dos usan suvalor por defecto
Solo se pasan las claves que escribes; las que omites conservan su valor por defecto. El = {} del final se ocupa de las llamadas que omiten el objeto entero.

En lugar de depender del orden de los parámetros, cada valor queda identificado por su clave, así que las llamadas se siguen leyendo bien aunque crezca la configuración. Las claves se emparejan por nombre, así que aunque las escribas en otro orden, como en { isUrgent: true, subject: "Reenvío" }, los valores que se reciben son los mismos.

Si omites = {}, obtienes un TypeError

Si escribes solo el patrón de desestructuración, como en { subject = "Aviso" }, una llamada que omite el segundo argumento lanza TypeError: Cannot read properties of undefined (reading 'subject'). Esto pasa porque intenta leer una clave de undefined, que es lo que contiene el parámetro omitido.

Cambia cómo se muestra el precio de un bento usando un objeto de configuración. lunchPrice ya está declarada.

① Escribe formatPrice, que recibe un precio sin impuestos y un objeto de configuración, y devuelve un string como "1058 yenes". Desestructura la configuración, con una tasa de impuesto de 0.08 y la unidad "yenes" como valores por defecto.

② Muestra el resultado de llamarla sin configuración.

③ Muestra el resultado con una tasa de 0.1 y la unidad "yenes (en el local)".

④ Muestra el resultado con una tasa de 0.

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado
QUIZ

Verificación de conocimientos

Responde cada pregunta una a una.

Pregunta 1Con const addFee = (price, fee = 500) => price + fee;, ¿qué devuelve addFee(1000, undefined)?

Pregunta 2¿Qué devuelve const countNotes = (id, ...notes) => notes.length; si la llamas como countNotes("A-1021")?

Pregunta 3¿Qué devuelve const send = (to, { urgent = false } = {}) => urgent; si la llamas como send("ana@example.com")?