Skip to content
This page has been auto-translated and may contain errors.View in English

Funciones

docs.scrimba.com

Digamos que estás fijando precios de artículos en un carrito. Tomas un número, le sumas el impuesto, lo redondeas y lo formateas con un símbolo de moneda. Luego necesitas hacerlo de nuevo para el siguiente artículo, y el siguiente. Copiar esos cuatro pasos en todas partes es cómo entran los errores: arregla el redondeo en un lugar y tienes tres más copias aún mal. Una función te deja nombrar ese bloque de pasos una sola vez y ejecutarlo cuando lo necesites. Este capítulo trata sobre escribir esos bloques, alimentarlos con entradas y obtener valores de vuelta.

Declarar y llamar una función

Una función es un bloque de código nombrado que puedes ejecutar cuando quieras. Escribes los pasos una vez, les das un nombre, y luego los ejecutas por ese nombre tantas veces como quieras.

Haces una con la palabra clave function:

js
function greet() {
  console.log("Hola");
}

Esa es toda la definición, pero aún no ha pasado nada. Escribir una función almacena los pasos; no los ejecuta. Para realmente ejecutarla, la llamas escribiendo su nombre seguido de paréntesis:

js
greet(); // Hola
greet(); // Hola

Dos líneas, dos saludos, desde un bloque de código. Definir y llamar son cosas separadas, y confundirlas es lo primero que detiene a la gente.

Una función envuelve un bloque de código bajo un nombre para que puedas ejecutarlo bajo demanda. La defines una vez con la palabra clave function, luego la llamas tantas veces como necesites:

js
function logDivider() {
  console.log("----------");
}

logDivider(); // ----------
logDivider(); // ----------

La definición configura los pasos pero no ejecuta nada. Una función solo se ejecuta cuando la llamas con (). El nombre por sí solo, logDivider, es el valor de la función; los paréntesis son lo que la invoca. Esa distinción importa en el momento en que comienzas a pasar funciones, porque logDivider y logDivider() son dos cosas diferentes: una es la función, la otra es el resultado de ejecutarla.

Una función es un bloque de código reutilizable vinculado a un nombre. La palabra clave function aquí crea una declaración de función: una función nombrada elevada al tope de su alcance, lo que significa que puedes llamarla en una línea por encima de donde la escribiste y aún funcionará.

js
countdown(); // funciona: las declaraciones se elevan

function countdown() {
  console.log("Preparado");
}

Esa elevación es específica de la forma de declaración; las otras formas de definir una función más adelante en este capítulo no la comparten. El nombre y la llamada son valores distintos: countdown se evalúa como la función misma (puedes guardarla, pasarla, compararla), mientras que countdown() se evalúa como lo que sea que produce ejecutar la función. Confundir los dos está detrás de una gran parte de la confusión "por qué esto es undefined", y mantenerlos separados es un hábito que vale la pena construir ahora, antes de que las funciones comiencen a pasarse como argumentos a otras funciones.

Dale a una función un nombre claro, tipo verbo, para lo que hace (greet, logDivider), el mismo cuidado que pones al nombrar variables. Una función que llamas en diez lugares es solo tan legible como su nombre.

JunoDeclarar y llamar una función Escribir una función almacena los pasos; no los ejecuta. Para ejecutarla, la llamas por nombre con paréntesis, como greet(). Esa división me confundía al principio: la definición es la receta, la llamada es realmente cocinar. Puedes llamar la misma función tantas veces como quieras.
JunoDeclarar y llamar una función Definir una función y llamarla son dos actos separados. El bloque function configura los pasos y no ejecuta nada hasta que agregues (). Mantén greet y greet() claros en tu cabeza: uno es el valor de la función, el otro es el resultado de ejecutarla. Esa diferencia muerde la primera vez que pasas una función a algún lugar.
JunoDeclarar y llamar una función La palabra clave function hace una declaración, que se eleva, así que puedes llamarla arriba de donde está. Eso es único para esta forma; las expresiones y las flechas no se elevan de la misma forma. Y countdown es la función mientras que countdown() es su resultado, una distinción que importa menos a medida que comienzas a pasar funciones como valores.

Parámetros y valores de retorno

Una función es más útil cuando puedes alimentarla con algo. Los valores que alimentas son parámetros: nombres listados entre paréntesis que se sostienen por lo que pases cuando la llames.

js
function greet(name) {
  console.log("Hola " + name);
}

greet("Santiago"); // Hola Santiago
greet("Lucía"); // Hola Lucía

name es el parámetro. Cuando llamas a greet("Santiago"), el texto "Santiago" se coloca en name para esa ejecución. Una función, resultados diferentes, dependiendo de lo que le pases.

Hasta ahora estas funciones imprimen algo. A menudo quieres que una función devuelva un valor en su lugar, para que puedas usarlo después. Eso es lo que return hace:

js
function double(number) {
  return number * 2;
}

const result = double(5);
console.log(result); // 10

return envía un valor fuera de la función. double(5) se convierte en 10, y puedes almacenarlo, imprimirlo o pasarlo. Imprimir muestra un valor en pantalla; devolver lo devuelve a tu código para que sigas usándolo.

Las funciones toman entradas a través de parámetros: los nombres entre paréntesis. Los valores que pasas en el sitio de la llamada son los argumentos que los llenan.

js
function priceWithTax(price, rate) {
  return price + price * rate;
}

const total = priceWithTax(40, 0.2);
console.log(total); // 48

price y rate son parámetros; 40 y 0.2 son los argumentos para esta llamada. La declaración return devuelve un valor a quien llamó a la función, así que priceWithTax(40, 0.2) se evalúa como 48 y puedes almacenarlo en total.

La diferencia entre registrar y devolver vale la pena fijar. Una función que llama a console.log te muestra algo y se evalúa como undefined; una función que usa return produce un valor con el que tu programa puede seguir trabajando. return también termina la función inmediatamente: ninguna línea después de ella se ejecuta. Si omites return completamente, la función aún devuelve undefined, que es una sorpresa común cuando esperabas un valor de vuelta y no obtuviste nada.

Los parámetros son las entradas nombradas de la función; los argumentos son los valores concretos vinculados a ellos en el sitio de la llamada. La declaración return produce el valor de salida de la función y termina la ejecución inmediatamente, así que funciona como una salida temprana:

js
function classify(score) {
  if (score < 0) {
    return "inválido";
  }
  if (score >= 60) {
    return "aprobado";
  }
  return "reprobado";
}

console.log(classify(72)); // aprobado
console.log(classify(-3)); // inválido

Cada return tanto produce un valor como detiene la función, así que una vez que una rama se ejecuta, nada por debajo de ella se ejecuta. Ese patrón de salida temprana (valida y abandona primero, maneja el caso principal al final) te mantiene fuera de condicionales profundamente anidados.

La distinción que importa en código real es entre una función que devuelve y una que solo produce un efecto secundario (escribir en la consola, cambiar algo fuera de sí misma). console.log realiza un efecto secundario y la función se evalúa como undefined; return produce un valor que el llamador puede componer. Una función sin return, o un return desnudo sin nada después de él, produce undefined. Llegar por un valor devuelto desde una función que solo registra es un error frecuente, y la solución es casi siempre return el valor e imprimirlo en el sitio de la llamada en su lugar.

JunoParámetros y valores de retorno Los parámetros son los nombres entre paréntesis que se sostienen por valores que pasas, así una función puede dar resultados diferentes. return devuelve un valor para que tu código lo use después. Imprimir con console.log lo muestra en pantalla; devolver lo devuelve a ti. Me tomó un tiempo sentir esa diferencia.
JunoParámetros y valores de retorno Los parámetros son los nombres en la definición, los argumentos son los valores que pasas en la llamada. return devuelve un valor al llamador y termina la función en el acto. Una función que solo registra se evalúa como undefined, así que si esperabas un valor y no obtuviste nada de vuelta, verifica si realmente lo devolviste.
JunoParámetros y valores de retornoreturn produce un valor y sale, lo que hace que validar-y-abandonar-temprano sea un patrón limpio. Observa la división entre devolver un valor y hacer un efecto secundario como console.log: la función de registro se evalúa como undefined. Cuando llegas por un resultado y no obtienes nada, casi siempre registraste donde querías devolver.

Funciones flecha

Hay una forma más corta de escribir una función, llamada una función flecha. La almacenas en una variable y usas la flecha => en lugar de la palabra clave function:

js
const add = (a, b) => {
  return a + b;
};

console.log(add(2, 3)); // 5

Cuando la función es una sola expresión como esta, puedes dejar caer las llaves y return y dejar que la flecha devuelva el valor para ti:

js
const add = (a, b) => a + b;

console.log(add(2, 3)); // 5

Ambas hacen lo mismo. Verás funciones flecha en todas partes en JavaScript moderno, así que ayuda reconocer la forma: un nombre, un =, las entradas, una flecha, y el cuerpo.

Una función flecha es una sintaxis más corta para escribir una función, almacenada en una variable con =>:

js
const formatPrice = (amount) => `$${amount.toFixed(2)}`;

console.log(formatPrice(9.5)); // $9.50

Cuando el cuerpo es una sola expresión, las funciones flecha tienen un retorno implícito: sin llaves, sin palabra clave return, el valor de la expresión se devuelve automáticamente. Agrega llaves y se comporta como un cuerpo de función normal, donde escribes return tú mismo:

js
const formatPrice = (amount) => {
  const rounded = amount.toFixed(2);
  return `$${rounded}`;
};

Las flechas son la opción idiomática para funciones cortas de propósito único, especialmente las pequeñas que pasas a otras funciones (las conocerás con métodos de array). Para una rutina nombrada, reutilizable, de nivel superior, una declaración function regular se lee claramente y funciona bien. Llega a las flechas cuando la brevedad ayuda y la función es pequeña.

Una función flecha es una expresión de función compacta escrita con =>. Con un cuerpo de una sola expresión tiene un retorno implícito (el valor de la expresión se devuelve sin palabra clave return); con un cuerpo entre llaves devuelves explícitamente, exactamente como una función regular.

js
const applyDiscount = (price, percent) => price * (1 - percent / 100);

console.log(applyDiscount(80, 25)); // 60

Tres formas de definir una función coexisten, y las diferencias son prácticas, no cosméticas:

  • Una declaración de función (function total() {}) se eleva, así que es invocable arriba de su definición.
  • Una expresión de función (const total = function () {}) no se eleva de la misma forma: la variable existe pero sostiene undefined hasta que la línea se ejecuta, así que llamarla antes lanza un error.
  • Una función flecha (const total = () => {}) se comporta como una expresión para elevarse y agrega una regla clave abajo.

La regla que distingue a las flechas: no vinculan su propio this. Una función regular obtiene un this determinado por cómo se llama; una flecha hereda this del código circundante en su lugar. Esa distinción no muerde hasta que escribas métodos en objetos, así que sostén el hecho y el razonamiento llega allí.

Dos herramientas más que vale la pena usar. Los parámetros por defecto dan un respaldo cuando falta un argumento, así la función se mantiene predecible:

js
const greet = (name = "ahí") => `Hola ${name}`;

console.log(greet("Santiago")); // Hola Santiago
console.log(greet()); // Hola ahí

Y el hábito que más te paga: prefiere funciones puras, aquellas cuya salida depende solo de sus entradas y que no cambian nada fuera de sí mismas (sin efectos secundarios). applyDiscount arriba es pura: mismas entradas, mismo resultado, siempre. Las funciones puras son aquellas que puedes probar aisladamente, razonar sin rastrear el resto del programa, y reutilizar sin sorpresas. No todo puede ser puro (algo tiene que registrar, obtener y cambiar estado), pero empujar tu lógica hacia funciones puras y mantener los efectos secundarios en los bordes es uno de los hábitos de mayor apalancamiento en el lenguaje.

JunoFunciones flecha Una función flecha es una forma más corta de escribir una función, almacenada en una variable con => en lugar de la palabra clave function. Cuando el cuerpo es una expresión, puedes dejar caer las llaves y return y la flecha devuelve el valor para ti. Verás esta forma constantemente, así que ponerse cómodo leyéndola se paga rápido.
JunoFunciones flecha Las flechas son la forma corta de funciones, y un cuerpo de una sola expresión te da un retorno implícito: sin llaves, sin return. Agrega llaves y escribes return tú mismo como normal. Llega a las flechas en funciones pequeñas de propósito único; una declaración function simple aún se lee muy bien para una rutina nombrada de nivel superior.
JunoFunciones flecha Las declaraciones se elevan, las expresiones y las flechas no, y las flechas también saltan su propio this, que solo importa una vez que escribes métodos de objeto. Apóyate en parámetros por defecto para argumentos faltantes, y empuja lógica hacia funciones puras cuya salida depende solo de sus entradas. Esas son aquellas que puedes probar solas y reutilizar sin rastrear todo el programa.

Hacia dónde van las funciones desde aquí

Las funciones son cómo dejas de repetirte: nombra un bloque de pasos, alimentalo con entradas, obtén un valor de vuelta. Una vez que eso hace clic, la mayoría del código que escribes se convierte en funciones pequeñas llamando a otras funciones, cada una haciendo un trabajo claro.

Desde aquí, las funciones aparecen en todas partes. Las pasarás a métodos de array para transformar listas de datos, las usarás para organizar la lógica ramificada de condicionales, y te apoyarás en la disciplina de nomenclatura que comenzaste con variables. La próxima vez que te atrapes a ti mismo copiando un bloque de código, esa es la señal de envolverlo en una función en su lugar.