Control structures
Control structures in JavaScript: Mastering if-else and switch
1. Introduction
Control structures are the skeleton of any program in JavaScript. They allow our code to make decisions, repeat actions, and, in general, control the flow of execution. Among these structures, if-else and switch stand out for their versatility and frequency of use. This blog will delve into these two structures, exploring their nuances, use cases, and best practices.
2. If-else control structure
2.1. Basic syntax of if-else
The if-else structure is fundamental in programming. It allows executing different blocks of code based on boolean conditions.
if (condición) {
// Código a ejecutar si la condición es verdadera
} else {
// Código a ejecutar si la condición es falsa
}
For example:
let temperatura = 22;
if (temperatura > 30) {
console.log('Hace calor');
} else {
console.log('La temperatura es agradable');
}
2.2. nested if-else
We can nest multiple if-else statements to handle multiple conditions:
let hora = 14;
if (hora < 12) {
console.log('Buenos días');
} else if (hora < 18) {
console.log('Buenas tardes');
} else {
console.log('Buenas noches');
}
However, it is important not to abuse excessive nesting, as it can make the code difficult to read and maintain.
2.3. Ternary operator
The ternary operator offers a concise syntax for writing simple if-else statements:
let resultado = condición ? valorSiVerdadero : valorSiFalso;
For example:
let edad = 20;
let mensaje = edad >= 18 ? 'Eres mayor de edad' : 'Eres menor de edad';
console.log(mensaje);
2.4. Truthy and Falsy in JavaScript
In JavaScript, values are evaluated as “truthy” or “falsy” in boolean contexts. This can be useful in if-else conditions:
let nombre = '';
if (nombre) {
console.log('El nombre está definido');
} else {
console.log('El nombre está vacío o no definido');
}
Falsy values in JavaScript:
false0""(empty string)nullundefinedNaN
All other values are considered truthy.
2.5. Logical operators in conditions
Los operadores lógicos && (AND), (OR) y ! (NOT) son útiles para crear condiciones más complejas:
let esDiaLaboral = true;
let tieneVacaciones = false;
if (esDiaLaboral && !tieneVacaciones) {
console.log('A trabajar');
} else {
console.log('A descansar');
}
3. Switch control structure
3.1. Basic syntax of the switch
The switch structure is useful when we need to compare a variable with multiple possible values:
switch (expresión) {
case valor1:
// Código a ejecutar si expresión === valor1
break;
case valor2:
// Código a ejecutar si expresión === valor2
break;
// ...
default:
// Código a ejecutar si ningún caso coincide
}
3.2. Multiple cases
We can group several cases if we want them to execute the same code:
let diaSemana = 3;
let tipodia;
switch (diaSemana) {
case 1:
case 2:
case 3:
case 4:
case 5:
tipodia = 'Día laboral';
break;
case 6:
case 7:
tipodia = 'Fin de semana';
break;
default:
tipodia = 'Día inválido';
}
console.log(tipodia);
3.3. The default case
The default case is executed when none of the previous cases match. It is good practice to include it to handle unexpected cases:
let fruta = 'Manzana';
let mensaje;
switch (fruta) {
case 'Naranja':
mensaje = 'Las naranjas cuestan $0.59 el kilo.';
break;
case 'Manzana':
mensaje = 'Las manzanas cuestan $0.32 el kilo.';
break;
case 'Plátano':
mensaje = 'Los plátanos cuestan $0.48 el kilo.';
break;
default:
mensaje = 'Lo siento, no tenemos ' + fruta + '.';
}
console.log(mensaje);
3.4. Switch with expressions
Starting from ECMAScript 2015 (ES6), switch can use expressions in the cases:
let edad = 22;
switch (true) {
case edad < 18:
console.log('Eres menor de edad');
break;
case edad >= 18 && edad < 65:
console.log('Eres un adulto');
break;
default:
console.log('Eres un adulto mayor');
}
3.5. Limitations of the switch
It is important to note that switch uses strict comparison (===). This means that the data type must also match:
let x = '1';
switch (x) {
case 1:
console.log('Uno');
break;
case '1':
console.log('Cadena uno');
break;
default:
console.log('Ninguno');
}
// Imprime: "Cadena uno"
4. Comparison between if-else and switch
4.1. Usage scenarios
if-elseis more flexible and can handle complex conditions.switchis ideal when comparing a single variable against known values.
4.2. Performance
In general, switch can be more efficient for many cases, especially in optimized JavaScript implementations.
4.3. Readability and maintainability
switch can be more readable when comparing a variable against many possible values. if-else is clearer for complex logical conditions.
5. Advanced control structures
5.1. Null coalescing operator (??)
Introduced in ECMAScript 2020, this operator is useful for providing a default value when a variable is null or undefined:
let usuario = null;
let nombreUsuario = usuario ?? 'Invitado';
console.log(nombreUsuario); // Imprime: "Invitado"
5.2. Optional chaining (?.)
Also introduced in ECMAScript 2020, it allows reading the value of a property located within a chain of connected objects without having to explicitly validate that each reference in the chain is valid:
let usuario = {
nombre: 'Juan',
direccion: {
calle: 'Calle Principal',
},
};
let calle = usuario?.direccion?.calle;
console.log(calle); // Imprime: "Calle Principal"
let codigoPostal = usuario?.direccion?.codigoPostal;
console.log(codigoPostal); // Imprime: undefined
6. Best practices
6.1. Clarity over brevity
Although concise code can be elegant, clarity should be the priority. For example, avoid nesting multiple ternary operators:
// Evita esto
let mensaje = edad < 18 ? 'Menor' : edad < 65 ? 'Adulto' : 'Adulto mayor';
// Prefiere esto
let mensaje;
if (edad < 18) {
mensaje = 'Menor';
} else if (edad < 65) {
mensaje = 'Adulto';
} else {
mensaje = 'Adulto mayor';
}
6.2. Avoid side effects in conditions
The conditions must be pure expressions without side effects:
// Evita esto
if (array.pop() === 3) {
// ...
}
// Prefiere esto
let ultimoElemento = array.pop();
if (ultimoElemento === 3) {
// ...
}
6.3. Use of constants to improve readability
Use constants with descriptive names for values that are used in conditions:
const EDAD_MINIMA = 18;
const EDAD_JUBILACION = 65;
if (edad >= EDAD_MINIMA && edad < EDAD_JUBILACION) {
console.log('En edad laboral');
}
6.4. Simplification of complex conditions
If you have very complex conditions, consider extracting them into separate functions:
function esEdadLaboral(edad) {
return edad >= EDAD_MINIMA && edad < EDAD_JUBILACION;
}
if (esEdadLaboral(usuario.edad)) {
console.log('En edad laboral');
}
7. Related design patterns
7.1. Strategy Pattern
This pattern can be an alternative to long chains of if-else:
const estrategias = {
A: () => {
console.log('Estrategia A');
},
B: () => {
console.log('Estrategia B');
},
C: () => {
console.log('Estrategia C');
},
};
function ejecutarEstrategia(tipo) {
return estrategias[tipo]();
}
ejecutarEstrategia('B'); // Imprime: "Estrategia B"
7.2. State Pattern
Similar to the Strategy pattern, but focused on changing the behavior of an object when its internal state changes:
class TrafficLight {
constructor() {
this.states = {
green: { next: 'yellow', action: () => console.log('Go!') },
yellow: { next: 'red', action: () => console.log('Slow down!') },
red: { next: 'green', action: () => console.log('Stop!') },
};
this.currentState = this.states.red;
}
change() {
this.currentState.action();
this.currentState = this.states[this.currentState.next];
}
}
const light = new TrafficLight();
light.change(); // Imprime: "Stop!"
light.change(); // Imprime: "Go!"
7.3. Configuration object
Instead of multiple parameters with default values, use a configuration object:
function crearUsuario({ nombre = 'Anónimo', edad = 0, esAdmin = false } = {}) {
return { nombre, edad, esAdmin };
}
let usuario1 = crearUsuario({ nombre: 'Ana', edad: 30 });
let usuario2 = crearUsuario({ esAdmin: true });
8. Debugging control structures
8.1. Use of console.log for debugging
Inserting console.log at key points can help understand the flow of your code:
if (condicion1) {
console.log('Condición 1 es verdadera');
// más código...
} else if (condicion2) {
console.log('Condición 2 es verdadera');
// más código...
} else {
console.log('Ninguna condición es verdadera');
// más código...
}
8.2. Browser debugging tools
Learn to use breakpoints in your browser’s developer tools. You can pause execution and examine variables in real time.
8.3. Common mistakes and how to avoid them
- Equality comparison: Use
===for strict comparison instead of==to avoid unexpected type conversions. - Confusing assignment with comparison: Make sure to use
==or===to compare, not=(which is for assignment). - Conditions always true or false: Check your conditions to make sure they can change.
- Incorrect use of parentheses in complex conditions: Use parentheses to clarify the order of evaluation in complex conditions.
Example of common mistakes and their corrections:
// Incorrecto
if (x = 5) { ... } // Asignación en lugar de comparación
// Correcto
if (x === 5) { ... }
// Incorrecto
if (someFunction()) { // Si someFunction() devuelve un objeto vacío, esto será siempre verdadero
...
}
// Correcto
if (someFunction() !== null) {
...
}
// Incorrecto (puede llevar a resultados inesperados)
if (a == 1 && b == 2 || c == 3) { ... }
// Correcto (clarifica el orden de evaluación)
if ((a === 1 && b === 2) || c === 3) { ... }
9. Performance and Optimization
9.1. Short-circuit evaluation
JavaScript utiliza la evaluación en cortocircuito para los operadores lógicos && y . Esto significa que si el resultado de la expresión se puede determinar por el primer operando, el segundo no se evalúa.
// El segundo operando solo se evalúa si el primero es verdadero
(condicion1 && condicion2)(
// El segundo operando solo se evalúa si el primero es falso
condicion1 || condicion2,
);
You can use this to optimize your code:
function saludar(nombre) {
nombre && console.log(`Hola, ${nombre}!`);
}
saludar('Ana'); // Imprime: Hola, Ana!
saludar(); // No imprime nada
9.2. Switch optimization vs literal object
In many cases, using a literal object can be more efficient than a switch:
// Usando switch
function getDiaSemana(numero) {
switch (numero) {
case 1:
return 'Lunes';
case 2:
return 'Martes';
case 3:
return 'Miércoles';
case 4:
return 'Jueves';
case 5:
return 'Viernes';
case 6:
return 'Sábado';
case 7:
return 'Domingo';
default:
return 'Número inválido';
}
}
// Usando objeto literal
const diasSemana = {
1: 'Lunes',
2: 'Martes',
3: 'Miércoles',
4: 'Jueves',
5: 'Viernes',
6: 'Sábado',
7: 'Domingo',
};
function getDiaSemana(numero) {
return diasSemana[numero] || 'Número inválido';
}
The literal object approach can be faster, especially for a large number of cases.
9.3. Performance Measurement
To measure the performance of different approaches, you can use console.time() and console.timeEnd():
console.time('switch');
for (let i = 0; i < 1000000; i++) {
getDiaSemanaSwich(Math.floor(Math.random() * 7) + 1);
}
console.timeEnd('switch');
console.time('objeto');
for (let i = 0; i < 1000000; i++) {
getDiaSemanaObjeto(Math.floor(Math.random() * 7) + 1);
}
console.timeEnd('objeto');
Remember that premature optimization is the root of all evil. Make sure your code is correct and readable before worrying about performance optimizations.
10. Conclusion
The control structures if-else and switch are fundamental in JavaScript and in programming in general. By mastering these structures, along with the advanced techniques and patterns we have discussed, you will be able to write more efficient, readable, and maintainable code.
Remember that the choice between if-else and switch (or even alternative approaches like literal objects or the strategy pattern) will depend on the specific context of your problem. There is no single solution that is the best for all cases.
As you continue developing your skills in JavaScript, you will find yourself using these control structures in increasingly sophisticated ways. Constant practice and code review (both your own and others’) will help you develop an instinct for choosing the most appropriate structure in each situation.
Finally, stay updated with the new features of JavaScript. As we have seen with the nullish coalescing operator (??) and optional chaining (?.), the language continues to evolve and offer new tools to make our code more expressive and robust.
Happy coding!