Aprende leyendo en orden

Declaraciones y expresiones de función — argumentos y return

Aprende las declaraciones de función, los argumentos y return, las expresiones de función y por qué olvidar return te devuelve undefined.

Si escribes la fórmula del precio con impuestos tal cual en tres lugares (el listado de productos, el carrito y la confirmación del pedido), cuando cambie la tasa de impuesto tendrás que corregir los tres, y la pantalla que se te pase seguirá mostrando el monto viejo.

Este artículo trata las declaraciones function, que le ponen nombre a un bloque de código para llamarlo; las expresiones de función, que guardan una función en una variable como un valor, y el valor que devuelve return.

Ponerle nombre al código para llamarlo — declaraciones function y parámetros

El cálculo del precio con impuestos es el mismo para todos los productos; lo único que cambia es el precio unitario. Si le pones nombre al cálculo y le pasas el precio en cada llamada, tanto el listado como el carrito solo tienen que llamarlo por ese nombre.

Una función (un bloque de código con nombre que puedes llamar cuantas veces quieras) se declara con la forma function nombre(parámetros) { cuerpo }. Los parámetros entre paréntesis son variables que reciben un valor en cada llamada, y el valor que escribes después de return (el valor de retorno) vuelve a quien hizo la llamada.

// Le pone el nombre taxIncluded al cálculo del precio con impuestos
function taxIncluded(price) {
  const tax = Math.floor(price * 0.1);
  return price + tax;             // Devuelve el valor calculado a quien llama
}

// Llama a la misma función con distintos precios unitarios
console.log(taxIncluded(1200));   // 1320
console.log(taxIncluded(980));    // 1078

// Si olvidas pasar un valor, price queda en undefined
console.log(taxIncluded());       // NaN
Tres llamadas distintas a la misma función
Definición detaxIncluded(price)taxIncluded(1200)taxIncluded(980)taxIncluded()price es 1200tax es 120price es 980tax es 98price es undefinedtax es NaNDevuelve 1320Devuelve 1078Devuelve NaNpasa un valorpasa un valorno pasa nada
Si cambia el valor que entra en price, también cambia el valor que se devuelve. En una llamada sin valor, price queda como undefined y el resultado es NaN.

price recibe solo el valor, así que da igual si escribes 1200 directamente o pasas una variable como itemPrice: la función se comporta exactamente igual.

Muestra la duración de las sesiones de una capacitación con el formato "X h Y min". lectureMinutes y practiceMinutes ya están declaradas.

① Declara una función que reciba minutos y devuelva un string con el formato "X h Y min".

② Muestra la duración de la clase como "Clase: X h Y min".

③ Muestra la duración de la práctica como "Práctica: X h Y min".

④ Muestra la suma de las dos como "Total: X h Y min".

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

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Recibir varios argumentos — se asignan por posición

Si el porcentaje de descuento cambia según el nivel de membresía o la promoción, una función que solo recibe el precio unitario no alcanza. Si quien llama también puede pasar el descuento, el cálculo sigue en un solo lugar y solo el porcentaje se decide en cada llamada.

Los parámetros se separan con comas. Los valores de la llamada se asignan en el orden en que los escribes: el primer valor va al primer parámetro y el segundo, al segundo. No se emparejan por nombre, así que si los pasas en otro orden el código sigue siendo válido y nada te avisa.

// Recibe un precio unitario y un porcentaje de descuento, y devuelve el precio con descuento
function applyDiscount(price, rate) {
  return Math.floor(price * (1 - rate));
}

// El primer valor va a price y el segundo, a rate
console.log(applyDiscount(2400, 0.2));    // 1920
console.log(applyDiscount(2400, 0.05));   // 2280

// Aunque inviertas el orden, el cálculo se ejecuta igual
console.log(applyDiscount(0.2, 2400));    // -480
Los argumentos se asignan por posición
Pasa(2400, 0.2)price es 2400rate es 0.2Calcula2400 × 0.8Devuelve 1920Pasa(0.2, 2400)price es 0.2rate es 2400Calcula0.2 × -2399Devuelve -480
Los mismos dos valores pasados a applyDiscount, cambiando solo el orden. Invertirlos no produce ningún error; lo único que cambia es el resultado.

En la línea de la llamada solo aparecen valores, así que leyendo applyDiscount(0.2, 2400) no vas a notar la confusión. Cuando obtengas un valor imposible como -480, compara el orden de los valores de la llamada con la definición de la función.

Calcula el monto a pagar de un pedido con dos funciones. unitPrice, quantity, shippingFee y couponAmount ya están declaradas.

① Declara una función que reciba un precio unitario y una cantidad, y devuelva el subtotal.

② Declara una función que reciba el subtotal, el costo de envío y el descuento, en ese orden, y devuelva el monto a pagar.

③ Pasa el valor de retorno de ① a ② y muestra "A pagar: X yenes".

④ Pon el costo de envío en 0 y muestra "Con envío gratis: X yenes".

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Tratar funciones como valores — expresiones de función y variables

El envío estándar y el envío exprés se calculan con fórmulas totalmente distintas. En lugar de escribir en cada llamada cuál usar, puedes guardar la función misma en una variable; así, el código que la usa solo tiene que llamar a esa variable.

Una expresión de función (una forma de asignar una función a una variable como un valor) se escribe como una asignación: const nombre = function (parámetros) { cuerpo };. Una declaración function ya está lista antes de que el código empiece a ejecutarse de arriba hacia abajo; en cambio, la variable de una expresión de función no recibe la función hasta que se ejecuta la línea de la asignación (el mecanismo se explica en el artículo sobre ámbito y hoisting).

// Una declaración function se puede llamar incluso desde líneas anteriores
console.log(expressShipping(1.5));       // 900

function expressShipping(weight) {
  return weight <= 2 ? 900 : 1300;
}

// Expresión de función: asigna una función a una variable como un valor
const normalShipping = function (weight) {
  return weight <= 2 ? 500 : 800;
};

// Sin paréntesis, la función misma pasa a otra variable
const feeRule = normalShipping;
console.log(feeRule(3));                 // 800
console.log(typeof feeRule);             // function
console.log(typeof normalShipping(3));   // number
¿Se puede llamar desde una línea anterior?
Declarada confunctionLlamada antesde la declaraciónYa está listaantes de ejecutarDevuelve 900Expresión guardadaen una variableLlamada antesde la asignaciónLa variable aún notiene la funciónSe detiene conReferenceError
Si la llamas en una línea anterior a la asignación, se detiene con ReferenceError. Una función guardada con una expresión de función no se puede llamar hasta que se ejecuta la línea de la asignación.

El mensaje de error completo es ReferenceError: Cannot access 'normalShipping' before initialization. Por otro lado, fíjate en la línea de feeRule del código de arriba: si asignas el nombre de una función sin paréntesis, la función misma pasa también a otra variable.

Con paréntesis y sin paréntesis
feeRule =normalShippingEntra lafunción mismatypeof esfunctionSe llama despuéscon feeRule(3)feeRule =normalShipping(3)Se ejecuta elcuerpo y entra 800typeof esnumberfeeRule(3) daTypeError
Sin paréntesis, en la variable entra la función misma; con paréntesis, entra el resultado de ejecutarla. Si le pones paréntesis a una función que querías llamar después, lo único que te queda es su valor de retorno.

Si declaras dos funciones con el mismo nombre, gana la última

Como las declaraciones function quedan listas antes de que el código se ejecute, si declaras dos con el mismo nombre, incluso una llamada en la línea justo después de la primera ejecuta el cuerpo de la última. La ejecución no se detiene, así que es fácil no darse cuenta de que hay otra función con el mismo nombre en otra parte del archivo. Con una expresión de función con const, la segunda declaración es un SyntaxError y el código ni siquiera empieza a ejecutarse.

Cambia el cálculo de la comisión de pago según el tipo de membresía. amount e isPremium ya están declaradas.

① Escribe como expresiones de función standardFee, que devuelve el 3%, y premiumFee, que devuelve el 1% (redondeando hacia abajo).

② Usa isPremium para elegir una de las dos y guárdala en applyFee.

③ Llama a applyFee y muestra "Comisión: X yenes".

④ Llama directamente a standardFee y muestra "Para miembros regulares: X yenes".

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Qué pasa si no hay valor de retorno — olvidar return

Si una función usa console.log, al llamarla aparece un texto en pantalla. Como algo se muestra, parece que funciona, pero si guardas su valor de retorno en una variable y lo usas, el string que esperabas no está ahí.

return devuelve un valor y, en ese mismo punto, termina la función. Una función sin return devuelve undefined: mostrar algo y devolver un valor son dos acciones distintas. console.log solo imprime en pantalla; no le entrega nada a quien llama.

// Con return: el texto vuelve a quien llama
function formatLabel(name, stock) {
  return `${name} (quedan ${stock})`;
  console.log("Inalcanzable");       // Está después de return, así que nunca se ejecuta
}

// Sin return: imprime, pero el valor de retorno es undefined
function printLabel(name, stock) {
  console.log(`${name} (quedan ${stock})`);
}

const label = formatLabel("Lámpara de escritorio", 3);
console.log(label);      // Lámpara de escritorio (quedan 3)

const printed = printLabel("Hub USB", 5);    // Hub USB (quedan 5)
console.log(printed);    // undefined
Cómo está escritaQué pasa al llamarlaQué recibe quien llama
return con el textoEl texto vuelve a quien llamaLámpara de escritorio (quedan 3)
Solo tiene console.logImprime, pero no devuelve nadaundefined
console.log tras returnNo se ejecuta nada tras returnEl texto vuelve igual

Si quien llama va a usar el valor, entrégaselo con return en lugar de mostrarlo.

Un salto de línea después de return hace que no se devuelva ningún valor

Si pones un salto de línea justo después de return y escribes el valor en la línea siguiente, JavaScript considera que la sentencia termina en la línea del return, y la función devuelve undefined. Es un error fácil de cometer cuando pasas un template literal largo a su propia línea para que se lea mejor. Empieza siempre el valor en la misma línea que return.

Suma el envío para obtener el monto a pagar. orderTotal, largeOrderTotal y shippingFee (una función que debería devolver el costo de envío) ya están declaradas.

① Muestra orderTotal más el envío como "A pagar: X yenes".

② Muestra largeOrderTotal con el mismo formato.

③ Declara shippingFeeFixed, que devuelva el costo de envío como número para cualquier monto.

④ Vuelve a mostrar lo de ① usando shippingFeeFixed.

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado
QUIZ

Verificación de conocimientos

Responde cada pregunta una a una.

Pregunta 1Si declaras function double(n) { n * 2; } y ejecutas console.log(double(4));, ¿qué se muestra?

Pregunta 2Si llamas a function fee(price, rate) { return price * rate; } como fee(0.2, 1000), ¿qué entra en price?

Pregunta 3¿Qué pasa si llamas a calc(1) en una línea anterior a la asignación const calc = function (n) { return n + 1; };?