JS 07: Funciones Avanzadas

Organiza tu código en bloques reutilizables y domina el alcance.

1. ¿Qué es una Función? La Máquina de Hacer Pasteles

Una función es un bloque de código reutilizable. Escribes las instrucciones una vez y las usas todas las veces que quieras.

Analogía: La Receta de Cocina

  • La Función: Es la receta escrita en el papel.
  • Parámetros (Ingredientes): Lo que necesitas para que funcione (Harina, Huevos).
  • El Cuerpo (Instrucciones): Batir, hornear, decorar.
  • Return (El Resultado): El pastel terminado que sale del horno.

2. Parámetros vs Argumentos: Aclarando Conceptos

Antes de seguir, es importante aclarar dos conceptos que a menudo se confunden:

⚠️ Aclaración Importante

  • Parámetro: Es el nombre de la variable que aparece en la definición de la función (cuando la creamos).
  • Argumento: Es el valor real que pasamos cuando llamamos a la función.
// En la DEFINICIÓN, "ingrediente" es un PARÁMETRO

function cocinar(ingrediente) {

    return "Cocinando " + ingrediente;

}



// En la LLAMADA, "tomate" es un ARGUMENTO

cocinar("tomate");



// También podemos usar una variable como argumento

let miVerdura = "zanahoria";

cocinar(miVerdura);  // "zanahoria" es el argumento

🚨 Error Común de Principiantes

Mito: "Si paso una variable como argumento, tiene que llamarse igual que el parámetro."

Realidad: ¡FALSO! Los nombres NO tienen que coincidir. Lo que importa es el valor, no el nombre.

// Esta función tiene un parámetro llamado "numero"

function duplicar(numero) {

    return numero * 2;

}



// ✅ CORRECTO: Puedo pasar una variable con CUALQUIER nombre

let edad = 25;

let resultado = duplicar(edad);  // edad (25) se copia a numero



// ✅ CORRECTO: Puedo pasar directamente un valor

let otro = duplicar(10);



// ✅ CORRECTO: Puedo usar otra variable con otro nombre

let precio = 50;

let precioDoble = duplicar(precio);  // precio (50) se copia a numero



// El parámetro "numero" es solo un contenedor temporal dentro de la función

// Recibe una COPIA del valor que le pasemos, sin importar cómo se llame fuera

Piénsalo así: El parámetro es como una caja con una etiqueta. El argumento es lo que metes dentro de esa caja. La etiqueta puede decir "numero", pero tú puedes meter dentro el contenido de cualquier variable (edad, precio, cantidad, etc.). Lo que importa es el contenido, no cómo se llama fuera.

3. Anatomía de una Función

// 1. Definimos la "máquina"

function saludar(nombre) {

    return "¡Hola, " + nombre + "! ¿Qué tal estás?";

}



// 2. Usamos la máquina (Llamar a la función)

let saludo1 = saludar("Ana");  // "¡Hola, Ana! ¿Qué tal estás?"

console.log(saludo1);

Parámetros por Defecto

¿Qué pasa si no le damos ingredientes a la máquina? Podemos poner valores por defecto.

function cocinarPasta(tipo = "Espaguetis") {

    return "Cocinando " + tipo;

}



console.log(cocinarPasta("Macarrones")); // "Cocinando Macarrones"

console.log(cocinarPasta());             // "Cocinando Espaguetis" (Usa el defecto)

4. Tipos de Funciones en JavaScript

JavaScript ofrece diferentes formas de crear funciones, cada una con sus propias características y casos de uso óptimos.

4.1 Declaración de Función (Function Declaration)

Es la forma clásica y más común. Se puede llamar antes de su definición gracias al hoisting.

Características

  • Definición: function nombre(params) { ... }
  • Llamada: nombre();
  • Hoisting: Se puede llamar antes de ser declarada
  • Uso ideal: Herramientas lógicas generales y funciones principales
// Definición

function mostrarAlerta(mensaje) {

    console.log("ALERTA: " + mensaje);

}



// Llamada directa

mostrarAlerta("Batería baja"); // "ALERTA: Batería baja"



// Ejemplo con return

function calcularIVA(precio) {

    return precio * 0.21;

}



let iva = calcularIVA(100); // 21

console.log(iva);

4.2 Expresión de Función (Function Expression)

La función se asigna a una variable. No tiene hoisting, solo se puede usar después de declararla.

Características

  • Definición: const miFuncion = function() { ... };
  • Llamada: miFuncion();
  • Hoisting: NO disponible antes de la declaración
  • Uso ideal: Cuando necesitas asignar funciones dinámicamente
// Definición

const obtenerDescuento = function(precio) {

    return precio * 0.10;

};



// Llamada mediante la variable

console.log(obtenerDescuento(100)); // 10



// Error si intentas usarla antes de declararla

// calcularPrecio(50); // ❌ ERROR: No se puede acceder antes de la inicialización

const calcularPrecio = function(precio) {

    return precio * 1.21;

};

4.3 Funciones Flecha (Arrow Functions)

Es la sintaxis moderna introducida en ES6. Más corta, limpia e ideal para operaciones rápidas.

Características

  • Definición: const miFuncion = () => { ... };
  • Llamada: miFuncion();
  • Return implícito: Si es una sola línea sin llaves, retorna automáticamente
  • Uso ideal: Operaciones rápidas, callbacks, map, filter, reduce

Evolución de la Sintaxis

// Nivel 1: Función Tradicional
// DEFINICIÓN
function sumar(a, b) {
    return a + b;
}
// USO
let resultado1 = sumar(5, 3);
console.log(resultado1); // 8



// Nivel 2: Función Flecha (Estándar)
// DEFINICIÓN
const sumarFlecha = (a, b) => {
    return a + b;
}
// USO
let resultado2 = sumarFlecha(10, 7);
console.log(resultado2); // 17



// Nivel 3: Return Implícito (¡Solo si es una línea!)
// Si quitas las llaves {}, el return es automático.
// DEFINICIÓN
const sumarRapido = (a, b) => a + b;
// USO
let resultado3 = sumarRapido(20, 15);
console.log(resultado3); // 35



// Nivel 4: Un solo parámetro (Sin paréntesis)
// DEFINICIÓN
const cuadrado = x => x * x;
// USO
let resultado4 = cuadrado(6);
console.log(resultado4); // 36

4.4 Funciones Anónimas (Uso común en Timers)

Son funciones sin nombre, típicamente usadas como callbacks en setTimeout, setInterval, o manejadores de eventos.

Características

  • No tienen nombre porque solo se necesitan en ese momento
  • La "llamada" la gestiona el navegador o el evento
  • Uso ideal: Callbacks de eventos o temporizadores únicos
// Ejemplo con setInterval

// La función anónima se ejecuta cada 3000ms

setInterval(function() {

    console.log("Han pasado 3 segundos más...");

}, 3000);



// Ejemplo con setTimeout

setTimeout(function() {

    console.log("Esto se ejecuta una sola vez después de 2 segundos");

}, 2000);



// También se puede usar con funciones flecha

setInterval(() => {

    console.log("Versión moderna con arrow function");

}, 3000);

4.5 IIFE (Immediately Invoked Function Expressions)

Se ejecutan inmediatamente al definirse. Son útiles para inicializar configuraciones que no quieres que nadie toque después.

Características

  • Se llaman a sí mismas con los paréntesis finales ()
  • Crean un ámbito privado (encapsulación)
  • Uso ideal: Inicialización de configuraciones, evitar contaminar el scope global
// Ejemplo clásico de IIFE

(function() {

    console.log("Configuración del sistema cargada.");

})(); // Se llama a sí misma con los paréntesis finales



// IIFE con parámetros

(function(nombre) {

    console.log("Bienvenido, " + nombre);

})("Usuario");



// IIFE con arrow function (ES6)

(() => {

    console.log("IIFE moderna con arrow function");

})();

4.6 IIFE + Timers: Capturando Valores en Bucles

Antiguamente, las IIFE eran vitales para que setTimeout o setInterval recordaran el valor correcto en un bucle. Esto creaba un "escudo" de datos para cada iteración.

⚠️ Problema Clásico con var

Sin IIFE, el temporizador siempre imprimiría el último valor del bucle porque para cuando se ejecuta, el bucle ya terminó.

// ❌ Problema: Sin IIFE (usando var)

for (var i = 1; i <= 3; i++) {

    setTimeout(function() {

        console.log("Número: " + i); // Imprimirá "4, 4, 4"

    }, i * 1000);

}



// ✅ Solución: Con IIFE para capturar el valor

for (var i = 1; i <= 3; i++) {

    // Usamos una IIFE para capturar el valor actual de 'i'

    (function(indiceCapturado) {

        setTimeout(function() {

            console.log("Contador: " + indiceCapturado); // "1, 2, 3"

        }, indiceCapturado * 1000);

    })(i); // Le pasamos 'i' a la IIFE inmediatamente

}



// 🎯 Solución Moderna: Usar 'let' en lugar de 'var'

// Con 'let', cada iteración tiene su propio scope

for (let i = 1; i <= 3; i++) {

    setTimeout(function() {

        console.log("Número: " + i); // Imprimirá "1, 2, 3"

    }, i * 1000);

}

4.7 Tabla Resumen: ¿Cuándo usar cada tipo?

Situación Función recomendada Ejemplo
Definir una herramienta lógica general Declaración function calcular() {...}
Asignar funciones dinámicamente Expresión const fn = function() {...}
Pasar una función rápida a map/filter Flecha (Arrow) arr.map(x => x * 2)
Programar una tarea repetitiva Anónima o Flecha setInterval(() => {...}, 1000)
Ejecutar código inmediatamente al cargar IIFE (function() {...})()
Capturar valores en bucles con timers IIFE + Timers o let (function(i) {...})(i)

5. Scope (Alcance): ¿Dónde viven mis variables?

Esto es crucial. Las variables no son visibles desde todas partes.

La Casa vs La Habitación

  • Global (La Casa): Si dejas las llaves en el salón, todos pueden cogerlas.
  • Local (La Habitación): Si tienes una tele en tu cuarto y cierras la puerta, solo tú puedes verla.
let variableGlobal = "Soy visible para todos";



function miFuncion() {

    let variableLocal = "Soy secreta";

    console.log(variableGlobal); // Funciona (Ve el exterior)

}



miFuncion();

// console.log(variableLocal); // ¡ERROR! No puedes ver dentro de la función
Volver a JS