Pregunta 1Con const addFee = (price, fee = 500) => price + fee;, ¿qué devuelve addFee(1000, undefined)?
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
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.
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
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.
| Llamada | Valor de status | Se muestra |
|---|---|---|
| notifyStatus("A-1021") | El valor por defecto, Recibido | A-1021: Recibido |
| notifyStatus("A-1021", "Enviado") | Enviado | A-1021: Enviado |
| notifyStatus("A-1021", undefined) | El valor por defecto, Recibido | A-1021: Recibido |
| notifyStatus("A-1021", null) | null | A-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";.
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 ()
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.
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
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.
Verificación de conocimientos
Responde cada pregunta una a una.
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")?