javascript

Estructuras de control en JavaScript

Domina las estructuras de control en JavaScript: condicionales, bucles y manejo de flujo para escribir código más eficiente y legible.

Estructuras de control en JavaScript
Photo by Will Porada on Unsplash

Las estructuras de control son fundamentales en cualquier lenguaje de programación. En JavaScript, nos permiten controlar el flujo de ejecución de nuestro código, tomar decisiones y repetir operaciones. En este artículo exploraremos las principales estructuras de control y cómo utilizarlas efectivamente.

Estructuras condicionales

if/else

La estructura condicional más básica y utilizada en JavaScript.

const edad = 18;

if (edad >= 18) {
  console.log('Eres mayor de edad');
} else {
  console.log('Eres menor de edad');
}

if/else if/else

Para múltiples condiciones:

const nota = 85;

if (nota >= 90) {
  console.log('Excelente (A)');
} else if (nota >= 80) {
  console.log('Bueno (B)');
} else if (nota >= 70) {
  console.log('Regular (C)');
} else {
  console.log('Necesitas mejorar (D)');
}

Operador ternario

Una forma concisa de escribir condicionales simples:

const edad = 20;
const mensaje = edad >= 18 ? 'Mayor de edad' : 'Menor de edad';
console.log(mensaje);

// Operador ternario anidado
const calificacion = 85;
const resultado = calificacion >= 90 ? 'A' :
                 calificacion >= 80 ? 'B' :
                 calificacion >= 70 ? 'C' : 'D';

Operador lógico AND (&&)

Útil para ejecutar código condicionalmente:

const usuario = { nombre: 'Juan', autenticado: true };

// Solo ejecuta si usuario existe y está autenticado
usuario && usuario.autenticado && console.log(`Hola ${usuario.nombre}`);

Operador lógico OR (||)

Para valores por defecto:

const nombre = null;
const nombrePorDefecto = nombre || 'Usuario anónimo';
console.log(nombrePorDefecto); // "Usuario anónimo"

// Con valores falsy
const configuracion = {
  puerto: process.env.PORT || 3000,
  baseDatos: process.env.DB_URL || 'localhost:5432'
};

Nullish coalescing (??)

Para manejar valores null y undefined específicamente:

const valor = null;
const valorPorDefecto = valor ?? 'Valor por defecto';
console.log(valorPorDefecto); // "Valor por defecto"

// Diferencia con ||
const cero = 0;
console.log(cero || 'fallback'); // "fallback"
console.log(cero ?? 'fallback'); // 0

Estructura switch

Ideal para múltiples condiciones basadas en el mismo valor:

const dia = 'lunes';

switch (dia) {
  case 'lunes':
    console.log('Inicio de semana');
    break;
  case 'viernes':
    console.log('¡Fin de semana!');
    break;
  case 'sabado':
  case 'domingo':
    console.log('Día de descanso');
    break;
  default:
    console.log('Día laboral');
}

Switch con múltiples casos

const mes = 3;

switch (mes) {
  case 12:
  case 1:
  case 2:
    console.log('Invierno');
    break;
  case 3:
  case 4:
  case 5:
    console.log('Primavera');
    break;
  case 6:
  case 7:
  case 8:
    console.log('Verano');
    break;
  case 9:
  case 10:
  case 11:
    console.log('Otoño');
    break;
  default:
    console.log('Mes inválido');
}

Switch con expresiones

const numero = 15;

switch (true) {
  case numero < 10:
    console.log('Número pequeño');
    break;
  case numero < 20:
    console.log('Número mediano');
    break;
  case numero < 50:
    console.log('Número grande');
    break;
  default:
    console.log('Número muy grande');
}

Bucles

for tradicional

// Bucle básico
for (let i = 0; i < 5; i++) {
  console.log(`Iteración ${i}`);
}

// Bucle con array
const frutas = ['manzana', 'banana', 'naranja'];
for (let i = 0; i < frutas.length; i++) {
  console.log(frutas[i]);
}

for…of

Para iterar sobre elementos de arrays y objetos iterables:

const colores = ['rojo', 'verde', 'azul'];

for (const color of colores) {
  console.log(color);
}

// Con strings
const texto = 'Hola';
for (const letra of texto) {
  console.log(letra);
}

// Con Map y Set
const mapa = new Map([['a', 1], ['b', 2]]);
for (const [clave, valor] of mapa) {
  console.log(`${clave}: ${valor}`);
}

for…in

Para iterar sobre propiedades de objetos:

const persona = {
  nombre: 'Juan',
  edad: 30,
  ciudad: 'Madrid'
};

for (const propiedad in persona) {
  console.log(`${propiedad}: ${persona[propiedad]}`);
}

// Con arrays (no recomendado)
const array = ['a', 'b', 'c'];
for (const indice in array) {
  console.log(`${indice}: ${array[indice]}`);
}

while

Ejecuta mientras la condición sea verdadera:

let contador = 0;
while (contador < 5) {
  console.log(`Contador: ${contador}`);
  contador++;
}

do…while

Ejecuta al menos una vez, luego verifica la condición:

let numero;
do {
  numero = Math.floor(Math.random() * 10);
  console.log(`Número generado: ${numero}`);
} while (numero !== 7);

Control de flujo en bucles

break

Sale completamente del bucle:

const numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

for (const numero of numeros) {
  if (numero === 5) {
    console.log('Encontrado el 5, saliendo...');
    break;
  }
  console.log(numero);
}

continue

Salta a la siguiente iteración:

const numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

for (const numero of numeros) {
  if (numero % 2 === 0) {
    continue; // Salta números pares
  }
  console.log(numero); // Solo imprime impares
}

Etiquetas (labels)

Para controlar bucles anidados:

outer: for (let i = 0; i < 3; i++) {
  inner: for (let j = 0; j < 3; j++) {
    if (i === 1 && j === 1) {
      break outer; // Sale del bucle exterior
    }
    console.log(`i: ${i}, j: ${j}`);
  }
}

Métodos de array para iteración

forEach

Ejecuta una función para cada elemento:

const numeros = [1, 2, 3, 4, 5];

numeros.forEach((numero, indice) => {
  console.log(`Elemento ${indice}: ${numero}`);
});

map

Crea un nuevo array con los resultados:

const numeros = [1, 2, 3, 4, 5];
const cuadrados = numeros.map(numero => numero * numero);
console.log(cuadrados); // [1, 4, 9, 16, 25]

filter

Crea un nuevo array con elementos que cumplen una condición:

const numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const pares = numeros.filter(numero => numero % 2 === 0);
console.log(pares); // [2, 4, 6, 8, 10]

find

Encuentra el primer elemento que cumple una condición:

const usuarios = [
  { id: 1, nombre: 'Juan', activo: true },
  { id: 2, nombre: 'María', activo: false },
  { id: 3, nombre: 'Pedro', activo: true }
];

const usuarioActivo = usuarios.find(usuario => usuario.activo);
console.log(usuarioActivo); // { id: 1, nombre: 'Juan', activo: true }

Estructuras de control avanzadas

try/catch/finally

Para manejo de errores:

function dividir(a, b) {
  try {
    if (b === 0) {
      throw new Error('División por cero');
    }
    return a / b;
  } catch (error) {
    console.error('Error:', error.message);
    return null;
  } finally {
    console.log('Operación completada');
  }
}

throw

Para lanzar errores personalizados:

function validarEdad(edad) {
  if (typeof edad !== 'number') {
    throw new TypeError('La edad debe ser un número');
  }
  if (edad < 0) {
    throw new RangeError('La edad no puede ser negativa');
  }
  return true;
}

Patrones comunes

Validación de entrada

function procesarUsuario(usuario) {
  if (!usuario) {
    throw new Error('Usuario requerido');
  }

  if (!usuario.nombre) {
    throw new Error('Nombre requerido');
  }

  if (!usuario.email || !usuario.email.includes('@')) {
    throw new Error('Email válido requerido');
  }

  return usuario;
}

Búsqueda con early return

function buscarUsuario(usuarios, id) {
  for (const usuario of usuarios) {
    if (usuario.id === id) {
      return usuario; // Early return
    }
  }
  return null; // No encontrado
}

Validación múltiple

function validarFormulario(datos) {
  const errores = [];

  if (!datos.nombre) errores.push('Nombre requerido');
  if (!datos.email) errores.push('Email requerido');
  if (datos.edad < 18) errores.push('Debe ser mayor de edad');

  if (errores.length > 0) {
    throw new Error(`Errores de validación: ${errores.join(', ')}`);
  }

  return true;
}

Mejores prácticas

1. Usar const y let en lugar de var

// ❌ Evitar
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // Imprime 3, 3, 3
}

// ✅ Correcto
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // Imprime 0, 1, 2
}

2. Preferir métodos de array sobre bucles manuales

// ❌ Bucle manual
const numeros = [1, 2, 3, 4, 5];
const cuadrados = [];
for (let i = 0; i < numeros.length; i++) {
  cuadrados.push(numeros[i] * numeros[i]);
}

// ✅ Método de array
const cuadrados = numeros.map(n => n * n);

3. Usar early returns para reducir anidación

// ❌ Anidado
function procesarUsuario(usuario) {
  if (usuario) {
    if (usuario.activo) {
      if (usuario.permisos) {
        return procesarPermisos(usuario.permisos);
      }
    }
  }
  return null;
}

// ✅ Early returns
function procesarUsuario(usuario) {
  if (!usuario) return null;
  if (!usuario.activo) return null;
  if (!usuario.permisos) return null;

  return procesarPermisos(usuario.permisos);
}

4. Usar switch para múltiples condiciones simples

// ❌ Múltiples if/else
function obtenerDiaSemana(numero) {
  if (numero === 1) return 'Lunes';
  else if (numero === 2) return 'Martes';
  else if (numero === 3) return 'Miércoles';
  // ...
  else return 'Día inválido';
}

// ✅ Switch
function obtenerDiaSemana(numero) {
  switch (numero) {
    case 1: return 'Lunes';
    case 2: return 'Martes';
    case 3: return 'Miércoles';
    // ...
    default: return 'Día inválido';
  }
}

Casos de uso avanzados

Bucle con async/await

async function procesarElementos(elementos) {
  for (const elemento of elementos) {
    try {
      const resultado = await procesarElemento(elemento);
      console.log('Procesado:', resultado);
    } catch (error) {
      console.error('Error procesando:', error);
    }
  }
}

Bucle con Promise.all

async function procesarElementosParalelo(elementos) {
  try {
    const promesas = elementos.map(elemento => procesarElemento(elemento));
    const resultados = await Promise.all(promesas);
    return resultados;
  } catch (error) {
    console.error('Error en procesamiento paralelo:', error);
  }
}

Generadores con bucles

function* generadorNumeros() {
  let i = 0;
  while (i < 5) {
    yield i++;
  }
}

for (const numero of generadorNumeros()) {
  console.log(numero);
}

Conclusión

Las estructuras de control son la base de la programación en JavaScript:

  • Condicionales: Para tomar decisiones basadas en condiciones
  • Bucles: Para repetir operaciones de manera eficiente
  • Control de flujo: Para manejar la ejecución del código
  • Métodos de array: Para operaciones funcionales más elegantes

Puntos clave a recordar

  1. Usa const/let en lugar de var
  2. Preferir métodos de array sobre bucles manuales cuando sea posible
  3. Early returns para reducir anidación
  4. Manejo de errores con try/catch
  5. Async/await para operaciones asíncronas

Dominar estas estructuras te permitirá escribir código más limpio, eficiente y mantenible.

Fuentes

¿Te fue útil este artículo?

Conversemos sobre cómo puedo ayudarte con tu proyecto

$ connect --to andrés
connection successful
WhatsApp ready
>
WhatsApp Chat directo

Respuesta en menos de 24h