⚡ Curso JavaScript · Nivel N2 Básico

Bloque 3: Funciones a fondo

Copiar y pegar código es de novatos; empaquetarlo en piezas reutilizables es de profesionales. En este bloque aprenderás a declarar funciones, devolver resultados, escribir flechas modernas, entender dónde viven tus variables (scope) y hasta pasar funciones como si fueran datos. Es el salto conceptual más grande del nivel principiante.

⏱ ≈ 12 horas 📚 4 lecciones ✍️ 16 ejercicios encadenados 📝 Examen final superable (≥ 70 %)
Lección 1 · Teoría

Declaración, parámetros y retorno

Una función es una máquina: le entra materia prima (parámetros), trabaja con ella y devuelve un resultado (return). Se define una vez y se usa mil veces.

anatomía de una función
function precioConIva(precio, tipoIva) {
  const total = precio * (1 + tipoIva);
  return total;
}

const factura = precioConIva(100, 0.21);
console.log(factura); // 121
Vocabulario que debes dominar
TérminoEs…Ejemplo
Parámetroel nombre en la DECLARACIÓN(precio, tipoIva)
Argumentoel valor real en la LLAMADAprecioConIva(100, 0.21)
returndevuelve el resultado y TERMINA la funciónreturn total;
⚠️ Dos errores clásicos: 1) confundir console.log() con return — loguear MUESTRA, return ENTREGA el valor para poder seguir trabajándolo. 2) Una función sin return devuelve undefined.

1.1 · return corta la ejecución

Nada que venga después de un return ejecutado se llega a leer. Esto permite escaleras de decisiones limpias, conectando con el Bloque 2:

cada rama sale por su return
function clasificar(nota) {
  if (nota >= 9) return "Sobresaliente";
  if (nota >= 5) return "Aprobado";
  return "Suspenso";   // el resto de casos
}
💡 Regla de oro: una función debe hacer UNA cosa. Si tu función se llama calcularYMostrar, sospecha: separa el cálculo (con return) del mostrar (con console.log).
Lección 2 · Teoría

Expresiones, flechas y valores por defecto

Además de declararse, una función puede nacer dentro de una constante. Y desde ES6 existe la forma compacta que verás en TODO código moderno: las arrow functions.

Tres formas de escribir la misma función «doble»
EstiloSintaxis
Declaraciónfunction doble(n) { return n * 2; }
Expresiónconst doble = function (n) { return n * 2; };
Flechaconst doble = n => n * 2;

2.1 · La flecha y sus tres niveles de compacidad

de formal a minimalista
// ① cuerpo clásico: llaves OBLIGAN a escribir return
const area = (base, altura) => {
  const resultado = base * altura;
  return resultado;
};

// ② retorno implícito: sin llaves, la expresión SE DEVUELVE sola
const areaCorta = (base, altura) => base * altura;

// ③ un único parámetro: paréntesis opcionales
const doble = n => n * 2;
⚠️ Trampa habitual: con llaves y SIN return, la flecha devuelve undefined. Si necesitas varias líneas, no olvides el return; si es una sola expresión, quita las llaves y olvídate.

2.2 · Parámetros por defecto

Un parámetro puede traer valor de fábrica: si la llamada no lo envía, se usa ese:

❌ sin default: undefined
function total(precio, iva) {
  return precio + precio * iva;
}

total(100);
// 100 + 100 * undefined → NaN 💥
✅ con default: siempre funciona
function total(precio, iva = 0.21) {
  return precio + precio * iva;
}

total(100);        // 121 ✔️ usa el default
total(100, 0.10); // 110 ✔️ usa el enviado
Lección 3 · Teoría

Scope, hoisting y closures

Las variables tienen vida propia limitada: el scope (ámbito) es la zona del programa donde existen. Lo declarado dentro de una función solo existe allí dentro.

global vs local
const web = "TécnicoWeb";  // GLOBAL: visible en todo el script

function visitar() {
  const minuto = 3;       // LOCAL: solo vive dentro de visitar()
  console.log(web);         // ✔️ leer hacia afuera SÍ se puede
}

console.log(minuto);       // ❌ ReferenceError: no existe aquí fuera
  • Hacia dentro, sí; hacia fuera, no: una función ve las variables globales y las suyas, pero nadie ve las locales de la función.
  • Por eso los parámetros son tan valiosos: entran datos por la puerta en vez de depender de globals.

3.1 · Hoisting: el elevador

Las declaraciones function se «elevan» al inicio de su ámbito: puedes llamarlas ANTES de escribirlas. Las const/let NO: usarlas antes de declararlas lanza error.

✅ function se eleva
saludar();  // ¡funciona!

function saludar() {
  console.log("Hola");
}
❌ const no se eleva
saludar();  // ReferenceError 💥

const saludar = () => {
  console.log("Hola");
};

3.2 · Closures: la mochila con memoria

Cuando una función nace DENTRO de otra, guarda acceso a las variables de su entorno aunque la exterior ya haya terminado. Eso es un closure:

un contador privado
function crearContador() {
  let cuenta = 0;          // vive en la mochila del closure

  return function () {
    cuenta++;
    return cuenta;
  };
}

const siguiente = crearContador();
console.log(siguiente()); // 1
console.log(siguiente()); // 2 — ¡recuerda su cuenta!
💡 Nadie más puede tocar cuenta: ni leerla ni resetearla. Los closures son la forma natural de crear estado PRIVADO. Volverán en el Bloque 8 con fuerza.
Lección 4 · Teoría

Callbacks y funciones puras

En JavaScript las funciones son valores: se guardan en constantes, se pasan por parámetros y se devuelven como resultados. Una función que recibe otra para ejecutarla recibe el nombre de higher-order function, y a la función pasada le llamamos callback.

la función como argumento
function operar(a, b, operacion) {
  return operacion(a, b);   // ejecuta LO QUE LLEGUE
}

function sumar(x, y) { return x + y; }
function multiplicar(x, y) { return x * y; }

console.log(operar(3, 4, sumar));        // 7
console.log(operar(3, 4, multiplicar)); // 12
console.log(operar(10, 5, (x, y) => x - y)); // 5 — callback anónimo
⚠️ Fíjate: sumar SIN paréntesis. Si escribieras sumar(3, 4) estarías EJECUTÁNDOLA y pasando su resultado; sin paréntesis pasas LA FUNCIÓN misma para que la ejecute quien reciba.

4.1 · Pura o impura: el sello de calidad

Dos condiciones para ser PURA
CondiciónSignifica…
Mismos argumentos → mismo resultado, SIEMPRENo depende de variables externas mutables ni del azar
No provoca efectos externosNo modifica globals, ni imprime, ni cambia nada ajeno a ella
✅ pura
function descuento(precio, pct) {
  return precio - precio * pct;
}
// descuento(100, .2) vale 80
// hoy, mañana y siempre
❌ impura
let ivaGlobal = 0.21;

function total(precio) {
  ivaGlobal = 0.25;              // efecto externo 💥
  return precio * (1 + ivaGlobal);
}
💡 Las funciones puras son fáciles de probar, reutilizar y mover de sitio. console.log() es impura (imprime = efecto externo): úsala PARA VER resultados, pero mantén el cálculo en funciones puras. Este principio domina el Bloque 5.
Práctica guiada

✍️ Los 16 ejercicios del bloque

Crea la carpeta js-b3/ y resuelve en orden — cada ejercicio encadena el anterior. Trabaja en archivos ej01.html, ej02.html… con un <script> al final del body y abre la consola (F12).

Sección A · Declaración y retorno

1 Tu primera función

Declara saludar(nombre) que imprima «Bienvenida/o, ___» con template literal. Llámala dos veces con nombres distintos.

Ver solución
function saludar(nombre) {
  console.log(`Bienvenida/o, ${nombre}`);
}

saludar("Ada");
saludar("Alan");

2 Máquina de dobles

doble(n) debe DEVOLVER (no imprimir) el número multiplicado por 2. Guarda dos resultados en constantes y muéstralos con un único console.log.

Ver solución
function doble(n) {
  return n * 2;
}

const d1 = doble(7);
const d2 = doble(125);
console.log(d1, d2); // 14 250

3 Área y perímetro

Dos funciones: area(base, altura) y perimetro(base, altura), ambas con return. Muéstralas juntas formateando con template literal.

Ver solución
function area(base, altura) {
  return base * altura;
}

function perimetro(base, altura) {
  return 2 * (base + altura);
}

const b = 4;
const h = 3;
console.log(`Área: ${area(b, h)} · Perímetro: ${perimetro(b, h)}`);

4 Boolean factory

esMayorDeEdad(edad) devuelve edad >= 18 directamente (¡una comparación YA es un boolean!). Fuera de la función, usa su resultado dentro de un if/else.

Ver solución
function esMayorDeEdad(edad) {
  return edad >= 18;
}

if (esMayorDeEdad(16)) {
  console.log("Entra");
} else {
  console.log("A casa, peque");
}

5 Returns en cascada

clasificarVelocidad(kmh) devuelve «Peatón» (< 10), «Ciudad» (< 60), «Autovía» (< 120) o «Excesivo». Cada rama con SU return. Prueba 5, 50, 100 y 180.

Ver solución
function clasificarVelocidad(kmh) {
  if (kmh < 10) return "Peatón";
  if (kmh < 60) return "Ciudad";
  if (kmh < 120) return "Autovía";
  return "Excesivo";
}

console.log(clasificarVelocidad(5));
console.log(clasificarVelocidad(50));
console.log(clasificarVelocidad(100));
console.log(clasificarVelocidad(180));

Sección B · Flechas y valores por defecto

6 De function a flecha

Rewrite del ejercicio 2 como arrow function de retorno implícito: const dobleFlecha = n => n * 2;. Comprueba que da los mismos resultados.

Ver solución
const dobleFlecha = n => n * 2;

console.log(dobleFlecha(7), dobleFlecha(125)); // 14 250 ✔️

7 Flecha con cuerpo

ficha(nombre, ciudad) como arrow MULTILÍNEA (con llaves) que construya y devuelva la frase «___ vive en ___». Recuerda: con llaves, return obligatorio.

Ver solución
const ficha = (nombre, ciudad) => {
  const frase = `${nombre} vive en ${ciudad}.`;
  return frase;
};

console.log(ficha("Ada", "Madrid"));

8 IVA configurable

precioTotal(precio, iva = 0.21) devuelve el precio con IVA. Llama a la función una vez SIN segundo argumento y otra CON 0.10.

Ver solución
function precioTotal(precio, iva = 0.21) {
  return precio + precio * iva;
}

console.log(precioTotal(100));       // 121 (default)
console.log(precioTotal(100, 0.10)); // 110

9 Saludos configurables

saludo(nombre, mensaje = "Hola") devuelve «mensaje, nombre!». Pruébalo con y sin segundo argumento.

Ver solución
function saludo(nombre, mensaje = "Hola") {
  return `${mensaje}, ${nombre}!`;
}

console.log(saludo("Ada"));                 // Hola, Ada!
console.log(saludo("Ada", "Bienvenida"));  // Bienvenida, Ada!

Sección C · Scope y closures

10 Mapa del scope

Declara una constante global web y dentro de una función una variable local visita. Dentro de la función imprime AMBAS; fuera, intenta imprimir la local y observa el ReferenceError.

Ver solución
const web = "TécnicoWeb";

function registrar() {
  let visita = "paseo 14:00";
  console.log(web);      // ✔️ global visible
  console.log(visita);   // ✔️ local visible aquí
}

registrar();
console.log(visita);     // ❌ ReferenceError: visita is not defined

11 Contadores gemelos

Reproduce crearContador() de la Lección 3.2. Crea DOS contadores (cA y cB) y demuestra que cada uno lleva SU propia cuenta alternando llamadas.

Ver solución
function crearContador() {
  let cuenta = 0;
  return () => {
    cuenta++;
    return cuenta;
  };
}

const cA = crearContador();
const cB = crearContador();

console.log(cA()); // 1
console.log(cB()); // 1 — mochila independiente
console.log(cA()); // 2

12 Fábrica con memoria

crearGenerador(prefijo) devuelve una función que devuelve «prefijo-001», «prefijo-002»… con contador interno. Genera dos IDs de pedido.

Ver solución
function crearGenerador(prefijo) {
  let consecutivo = 0;
  return () => {
    consecutivo++;
    return `${prefijo}-${String(consecutivo).padStart(3, "0")}`;
  };
}

const siguientePedido = crearGenerador("PED");
console.log(siguientePedido()); // PED-001
console.log(siguientePedido()); // PED-002

Sección D · Callbacks y pureza

13 El ejecutor

ejecutar(accion) debe llamar a accion() tres veces. Pásale como callback una flecha que imprima «Tic».

Ver solución
function ejecutar(accion) {
  accion();
  accion();
  accion();
}

ejecutar(() => console.log("Tic")); // Tic × 3

14 Repetidor configurable

repetirVeces(n, accion) ejecuta accion(i) en cada vuelta, pasándole el índice. Llámalo con una flecha que muestre «Vuelta i de n».

Ver solución
function repetirVeces(n, accion) {
  for (let i = 1; i <= n; i++) {
    accion(i);
  }
}

repetirVeces(4, i => console.log(`Vuelta ${i} de 4`));

15 Calculadora por callback

Reproduce el patrón operar(a, b, operacion) de la Lección 4. Declara sumar, restar y multiplicar, y llama a operar con las tres (¡sin paréntesis al pasarlas!).

Ver solución
function sumar(x, y) { return x + y; }
function restar(x, y) { return x - y; }
function multiplicar(x, y) { return x * y; }

function operar(a, b, operacion) {
  return operacion(a, b);
}

console.log(operar(10, 3, sumar));       // 13
console.log(operar(10, 3, restar));      // 7
console.log(operar(10, 3, multiplicar)); // 30

16 🏆 Proyecto integrador: caja-registradora.html

Crea caja-registradora.html. Requisitos mínimos:

  • Dos funciones declaradas con comentarios /** … */ encima.
  • total(precioBase, unidades = 1): pura, con parámetro por defecto.
  • Una arrow function para formatear el ticket.
  • Una higher-order function que reciba un callback (aplicarDescuento(importe, criterio)).
  • Llama a alguna función al menos dos veces.
  • Nada de globals mutables: todo entra por parámetros y sale por return.
Ver solución modelo
/**
 * Calcula el importe bruto.
 */
function total(precioBase, unidades = 1) {
  return precioBase * unidades;
}

/**
 * Aplica descuento solo si el criterio lo aprueba.
 */
function aplicarDescuento(importe, criterio) {
  if (criterio(importe)) {
    return importe * 0.9;
  }
  return importe;
}

const etiqueta = importe => `Total: ${importe.toFixed(2)} €`;

console.log(etiqueta(aplicarDescuento(total(25, 2), i => i > 50)));
console.log(etiqueta(total(9)));
Evaluación

📝 Examen del Bloque 3

Necesitas ≥ 70 % (10/14) para superar el bloque.

1 · ¿Palabra clave para declarar una función clásica?
2 · Una función sin return devuelve…
3 · El nombre en la DECLARACIÓN es… y el valor en la LLAMADA es…
4 · Al ejecutarse un return…
5 · ¿Cuál equivale a function doble(n){ return n * 2; }?
6 · Una flecha CON llaves y SIN return devuelve…
7 · Con f(precio, iva = 0.21), al llamar f(100), iva vale…
8 · Una variable declarada DENTRO de una función…
9 · ¿Qué permite llamar una función ANTES de declararla?
10 · En crearContador(), tras terminar la exterior…
11 · Para pasar una función como callback escribo…
12 · Una función PURA cumple…
13 · console.log() dentro del cálculo hace a la función…
14 · function f(){ return 10; return 20; } — f() devuelve…

Parte B · Reto práctico final

Construye el ejercicio 16 («caja-registradora.html») sin mirar soluciones y valida tú mismo/a estos criterios. Mínimo para aprobar: 7 de 8 casillas.

Cumplidos: 0 / 8

🎉 ¿Superaste las dos partes? Enhorabuena: has aprobado el Bloque 3 de JavaScript. Tu código ya vive en piezas reutilizables con memoria propia. En el Bloque 4 esas funciones conocerán a los objetos y los arrays: datos con estructura.