Funciones: Declaraciones, Expresiones y Arrow Functions
Objetivos de aprendizajeAl finalizar esta lección serás capaz de:
- Crear funciones usando declaraciones, expresiones y arrow functions
- Entender parámetros, argumentos, valores por defecto y rest parameters
- Trabajar con el valor de retorno (return) de forma efectiva
- Distinguir cuándo usar cada tipo de función
- Aplicar el principio de responsabilidad única para escribir funciones limpias
1. Declaración de funciones y sus variantes
Las funciones son bloques de código reutilizables. Son la base de cualquier programa bien organizado. En vez de repetir código, lo encapsulás en una función y la llamás cuando la necesites.
Declaración de función (function declaration)
La forma más clásica de crear funciones:
function saludar(nombre) {
return `¡Hola, ${nombre}! Bienvenido a Cursalo.`;
}
console.log(saludar("Ana")); // "¡Hola, Ana! Bienvenido a Cursalo."
console.log(saludar("Carlos")); // "¡Hola, Carlos! Bienvenido a Cursalo."
Hoisting: Las declaraciones de función se "elevan" al inicio del scope, lo que significa que podés llamarlas antes de declararlas:
// ✅ Funciona gracias al hoisting
console.log(sumar(3, 5)); // 8
function sumar(a, b) {
return a + b;
}
Expresión de función (function expression)
Asignás una función a una variable:
const multiplicar = function(a, b) {
return a * b;
};
console.log(multiplicar(4, 7)); // 28
// ❌ NO tiene hoisting
// console.log(dividir(10, 2)); // ReferenceError
const dividir = function(a, b) {
return a / b;
};
Arrow Function (=>)
Introducidas en ES6, son la forma más concisa de escribir funciones:
// Sintaxis completa
const saludar = (nombre) => {
return `¡Hola, ${nombre}!`;
};
// Si solo hay un parámetro, los paréntesis son opcionales
const duplicar = n => {
return n * 2;
};
// Si el cuerpo es una sola expresión, podés omitir {} y return
const triplicar = n => n * 3;
const sumar = (a, b) => a + b;
const obtenerFecha = () => new Date().toLocaleDateString();
console.log(triplicar(5)); // 15
console.log(sumar(3, 7)); // 10
console.log(obtenerFecha()); // "21/2/2026"
// Retornar un objeto literal (necesita paréntesis extra)
const crearUsuario = (nombre, edad) => ({ nombre, edad, activo: true });
console.log(crearUsuario("Ana", 25)); // { nombre: "Ana", edad: 25, activo: true }
¿Cuándo usar cada una?
| Tipo | Usar cuando |
|---|---|
Declaración (function nombre()) |
Funciones principales, que necesitan hoisting |
Expresión (const f = function()) |
Callbacks, cuando querés evitar hoisting |
Arrow (const f = () => {}) |
Callbacks cortos, métodos de array (.map, .filter), funciones simples |
// Arrow functions brillan en métodos de array
const numeros = [1, 2, 3, 4, 5];
const dobles = numeros.map(n => n * 2); // [2, 4, 6, 8, 10]
const pares = numeros.filter(n => n % 2 === 0); // [2, 4]
const suma = numeros.reduce((acc, n) => acc + n, 0); // 15
// Encadenar métodos con arrows
const resultado = numeros
.filter(n => n > 2)
.map(n => n * 10)
.reduce((acc, n) => acc + n, 0);
console.log(resultado); // 120 (30 + 40 + 50)
2. Parámetros y argumentos a fondo
Parámetros por defecto
function crearPerfil(nombre, edad = 18, pais = "Argentina") {
return { nombre, edad, pais };
}
console.log(crearPerfil("Ana")); // { nombre: "Ana", edad: 18, pais: "Argentina" }
console.log(crearPerfil("Carlos", 30)); // { nombre: "Carlos", edad: 30, pais: "Argentina" }
console.log(crearPerfil("Luis", 25, "México")); // { nombre: "Luis", edad: 25, pais: "México" }
// Los defaults pueden ser expresiones
function crearId(prefijo = "USR", numero = Math.floor(Math.random() * 10000)) {
return `${prefijo}-${numero}`;
}
console.log(crearId()); // "USR-7423" (número aleatorio)
console.log(crearId("ADM")); // "ADM-1856"
Rest parameters (...args)
Permiten recibir un número indefinido de argumentos como un array:
function sumarTodos(...numeros) {
return numeros.reduce((total, n) => total + n, 0);
}
console.log(sumarTodos(1, 2, 3)); // 6
console.log(sumarTodos(10, 20, 30, 40, 50)); // 150
// Combinar parámetros normales con rest
function registrarVenta(vendedor, ...productos) {
console.log(`Vendedor: ${vendedor}`);
console.log(`Productos vendidos: ${productos.length}`);
productos.forEach(p => console.log(` - ${p}`));
}
registrarVenta("María", "Laptop", "Mouse", "Teclado");
Destructuring en parámetros
// En vez de acceder a propiedades con punto
function mostrarUsuario({ nombre, edad, email = "no proporcionado" }) {
console.log(`Nombre: ${nombre}`);
console.log(`Edad: ${edad}`);
console.log(`Email: ${email}`);
}
mostrarUsuario({ nombre: "Ana", edad: 28, email: "[email protected]" });
mostrarUsuario({ nombre: "Carlos", edad: 35 }); // email usa default
// Muy común en React y en configuraciones
function configurarApp({ tema = "oscuro", idioma = "es", notificaciones = true } = {}) {
console.log(`Tema: ${tema}, Idioma: ${idioma}, Notif: ${notificaciones}`);
}
configurarApp(); // Usa todos los defaults
configurarApp({ tema: "claro" }); // Solo cambia el tema
configurarApp({ idioma: "en", tema: "claro" }); // Cambia idioma y tema
3. Return, scope y closures
Return: devolviendo valores
// Una función sin return devuelve undefined
function sinReturn() {
const x = 42;
}
console.log(sinReturn()); // undefined
// Return detiene la ejecución de la función
function verificarEdad(edad) {
if (edad < 0) return "Edad inválida";
if (edad < 18) return "Menor de edad";
if (edad < 65) return "Adulto";
return "Adulto mayor";
// Este código nunca se ejecuta
console.log("Esto nunca se imprime");
}
// Retornar múltiples valores (usando un objeto)
function calcularEstadisticas(numeros) {
const suma = numeros.reduce((a, b) => a + b, 0);
const promedio = suma / numeros.length;
const minimo = Math.min(...numeros);
const maximo = Math.max(...numeros);
return { suma, promedio, minimo, maximo };
}
const stats = calcularEstadisticas([10, 20, 30, 40, 50]);
console.log(stats); // { suma: 150, promedio: 30, minimo: 10, maximo: 50 }
// Destructuring del return
const { promedio, maximo } = calcularEstadisticas([5, 10, 15]);
console.log(promedio); // 10
console.log(maximo); // 15
Scope: alcance de las variables
// Scope global: visible en todo el programa
const globalVar = "Soy global";
function ejemplo() {
// Scope de función: solo visible dentro de la función
const funcionVar = "Soy de la función";
console.log(globalVar); // ✅ Accede a global
console.log(funcionVar); // ✅ Accede a local
if (true) {
// Scope de bloque: solo visible dentro del if
const bloqueVar = "Soy del bloque";
console.log(bloqueVar); // ✅
console.log(funcionVar); // ✅
console.log(globalVar); // ✅
}
// console.log(bloqueVar); // ❌ ReferenceError
}
ejemplo();
// console.log(funcionVar); // ❌ ReferenceError
Closures: funciones que recuerdan
Un closure es una función que "recuerda" las variables del scope donde fue creada, incluso después de que ese scope haya terminado:
function crearContador() {
let cuenta = 0; // Esta variable queda "encerrada" en el closure
return {
incrementar: () => ++cuenta,
decrementar: () => --cuenta,
obtenerValor: () => cuenta,
};
}
const contador = crearContador();
console.log(contador.incrementar()); // 1
console.log(contador.incrementar()); // 2
console.log(contador.incrementar()); // 3
console.log(contador.decrementar()); // 2
console.log(contador.obtenerValor()); // 2
// Cada instancia tiene su propia cuenta
const otroContador = crearContador();
console.log(otroContador.obtenerValor()); // 0 (independiente)
// Closure práctico: crear funciones multiplicadoras
function crearMultiplicador(factor) {
return (numero) => numero * factor;
}
const duplicar = crearMultiplicador(2);
const triplicar = crearMultiplicador(3);
const porcentaje = crearMultiplicador(0.01);
console.log(duplicar(5)); // 10
console.log(triplicar(5)); // 15
console.log(porcentaje(150)); // 1.5
Funciones de orden superior
Funciones que reciben o devuelven otras funciones:
// Función que recibe otra función como parámetro
function aplicarOperacion(numeros, operacion) {
return numeros.map(operacion);
}
const nums = [1, 2, 3, 4, 5];
console.log(aplicarOperacion(nums, n => n * 2)); // [2, 4, 6, 8, 10]
console.log(aplicarOperacion(nums, n => n ** 2)); // [1, 4, 9, 16, 25]
console.log(aplicarOperacion(nums, n => `$${n}`)); // ["$1", "$2", "$3", "$4", "$5"]
// Función que retorna una función (factory)
function crearValidador(min, max) {
return (valor) => valor >= min && valor <= max;
}
const esEdadValida = crearValidador(0, 120);
const esPrecioValido = crearValidador(0.01, 999999);
const esNotaValida = crearValidador(1, 10);
console.log(esEdadValida(25)); // true
console.log(esEdadValida(-5)); // false
console.log(esPrecioValido(0)); // false
console.log(esNotaValida(7)); // true
Errores comunes de principiantes
Olvidar el
return: Sin return, la función devuelveundefined. Si tu función debe producir un resultado, siempre usá return.Confundir parámetros con argumentos: Los parámetros son las variables en la definición; los argumentos son los valores que pasás al llamar la función.
Arrow functions con cuerpo de una línea y
{}: Si usás{}en una arrow, necesitásreturnexplícito.n => n * 2funciona, peron => { n * 2 }devuelve undefined.Mutar datos pasados como argumento: Los objetos y arrays se pasan por referencia. Si modificás un objeto dentro de una función, el original también cambia.
Funciones demasiado largas: Si tu función tiene más de 20-30 líneas, probablemente hace demasiadas cosas. Dividila en funciones más pequeñas.
Puntos clave de esta lección
- Las declaraciones de función tienen hoisting; las expresiones y arrows no.
- Las arrow functions son ideales para callbacks y funciones cortas.
- Los parámetros por defecto evitan undefined cuando no se pasan argumentos.
- Rest parameters (
...args) permiten recibir un número variable de argumentos. - El scope determina dónde son accesibles las variables (global > función > bloque).
- Los closures permiten que funciones "recuerden" variables de su scope de creación.
- Las funciones de orden superior reciben o devuelven funciones — son clave en JavaScript moderno.
Quiz de autoevaluación
1. ¿Qué devuelve una función sin return?
a) null
b) 0
c) undefined
d) false
2. ¿Cuál es la sintaxis correcta de una arrow function con un parámetro y retorno implícito?
a) (n) => { n * 2 }
b) n => n * 2
c) n -> n * 2
d) function(n) => n * 2
3. ¿Qué es un closure?
a) Una función que se cierra después de ejecutarse
b) Una función que recuerda las variables de su scope de creación
c) Un tipo de loop
d) Un error de JavaScript
4. ¿Qué permite rest parameters (...)?
a) Pausar la ejecución
b) Recibir un número indefinido de argumentos como array
c) Expandir un array
d) Crear variables globales
5. ¿Las declaraciones de función tienen hoisting?
a) Sí, se pueden llamar antes de declararlas
b) No, deben declararse antes de llamarlas
c) Solo con const
d) Solo en modo estricto
Respuestas: 1-c, 2-b, 3-b, 4-b, 5-a
Revisemos los puntos más importantes de esta lección antes de continuar.
Ejercicio práctico
Misión: Mini-framework de validación
Creá un conjunto de funciones que formen un mini-sistema de validación reutilizable:
crearValidador(reglas)— función factory que recibe un array de reglas y retorna una función validadora- Cada regla es un objeto
{ campo, validar: (valor) => boolean, mensaje } - La función retornada recibe un objeto de datos y retorna
{ esValido, errores } - Creá validadores para: requerido, email, mínimo de caracteres, solo números
// Resultado esperado:
const validarRegistro = crearValidador([
{ campo: "nombre", validar: v => v && v.length >= 2, mensaje: "Nombre muy corto" },
{ campo: "email", validar: v => v && v.includes("@"), mensaje: "Email inválido" },
{ campo: "edad", validar: v => v >= 13 && v <= 120, mensaje: "Edad fuera de rango" },
]);
const resultado = validarRegistro({ nombre: "A", email: "test", edad: 10 });
console.log(resultado);
// { esValido: false, errores: ["Nombre muy corto", "Email inválido", "Edad fuera de rango"] }
- Comprendo el concepto principal y puedo explicarlo con mis palabras
- Entiendo cómo aplicarlo en mi situación específica
- Necesito repasar algunas partes antes de continuar
- Quiero ver más ejemplos prácticos del tema