Estructuras de control

En Javascript, tenemos varias estructuras de control que nos permiten modificar el flujo de ejecución de nuestro código dependiendo de si se cumple o no cierta condición.

Podemos dividir estas estructuras de control en dos grandes grupos: condicionales y ciclos.

If-else

Muchas veces, debemos tomar la decisión de si ejecutaremos o una acción determinada en nuestro programa, es decir, dada cierta condición podemos determinar si se ejecutará un bloque de código u otro.

Para esto existen los Condicionales.

La estructura de control if else, es posiblemente la más utilizada en cualquier lenguaje de programación pues nos permite ejecutar un determinado bloque de código dependiendo de si una condición es verdadera o falsa.

La estructura if-else nos permite dividir el código (comportamiento) de cierta parte del programa dependiendo de el cumplimiento de cierta condición.

Podemos ver un ejemplo a continuación:

let i = 10;
if(i > 5){
    console.log(`El valor se i es mayor a 5`);
}
else{
    console.log(`El valor se i es menor a 5`);
}

Podemos ver que, en efecto, la condición entre paréntesis se evalúa a true, por lo que se ejecuta el código dentro del primer bloque de código.

La sentencia else le indica al código que, de no cumplirse la condición, se ejecutará el siguiente bloque de código. Podemos ver que solamente puede ejecutarse uno de los bloques a la vez pues el valor de la condición solamente puede ser o true o false, por lo que ambos bloques de código nunca se ejecutaran al mismo tiempo.

No siempre es necesario que se ejecute una acción cuando la condición no se cumple, por lo que también es posible utilizar la sentencia if por si sola.

let i = 2;
if(i % 2 == 0)
    console.log(`${i} es número par`);

Si la condición del if no se cumple simplemente no pasada nada.

Como podrás notar en el ejemplo de arriba no hemos colocado llaves para delimitar el bloque de código que se ejecuta cuando la condición es cierta. Esto no es obligatorio y solamente es posible si el bloque de código esta compuesto por una línea, de lo contrario, el bloque debe estar delimitado por llaves.

Veamos un pequeño ejercicio donde utilizamos la estructura If-else.

Vamos a diseñar un script que nos permita saber si un año es bisiesto. Un año bisiesto debe ser divisible por 4 y no debe ser divisible por 100, excepto que también sea divisible por 400.

    let year = prompt("Escribe un año");
    if(year % 4 == 0){
        if((year%100 != 0) || (year%400 == 0)){
            console.log("Bisiesto");
        }else{
            console.log("No bisiesto");
        }
    }else{
        console.log("No es bisiesto");
    }

En este caso, tenemos anidadas dos estructuras if else; si la condición del primer if se cumple ejecutamos una segunda estructura de control.

En algunas ocasiones, puede ocurrir que necesitemos checar más de una condición y que, con utilizar un else no sea suficiente. Para esto, podemos utilizar la sentencia else if que nos permite checar una condición si es que la condición en el if inicial no se cumplió:

if(i > 5){
    //Bloque de código
}
else if(i % 2 == 0){
    //Bloque de código
}
else{
    //Bloque de código
}

En este caso, si i no es mayor a 5, entonces checaremos si i es par; si tampoco se cumple esta condición, se ejecutara el último bloque de código.

Operador condicional

En el capítulo anterior mencionamos al operador ternario o condicional, un operador que utiliza 3 operandos y en algunas ocasiones puede utilizarse como una abreviación de la estructura if else.

let i = (15 % 3 == 0) ? "Si es múltiplo" : "No es múltiplo"; //i = "Si es múltiplo"

En este ejemplo tenemos que, si 15 es múltiplo de 3, el valor de i será igual a la primera expresión, de lo contrario sería igual a la segunda expresión.

Switch

La estructura switch surge de un concepto conocido como caza de patrones: la idea es que dada una expresión esta se evalúe y se ejecute el código correspondiente a ese caso. Si no encontramos un caso que coincida con la expresión, se ejecutará el bloque de código definido es default, aunque no es obligatorio definir este caso.

Los pasos que se ejecutan en una estructura switch son:
1. La expresión del switch es evaluada.
2. Se compara el valor resultante con cada uno de los casos (case).
3. Si existe una coincidencia, se ejecuta el bloque de código correspondiente al case y la ejecución de la estructura termina al llegar al break.
4. En otro caso, si no hay coincidencias, se ejecuta el caso por defecto (default).

brake es una palabra reservada del lenguaje que nos permite terminar de forma instantánea la ejecución de un bloque de código.

En el caso de la estructura switch es esencial colocar un break al final de cada caso (incluyendo el default), pues de lo contrario, se ejecutarían también los casos siguientes.

    let n = 10
    switch(n){
        case 10:
            console.log("Es 10")
        case 20:
            console.log("Es 20")
            break;
        default:
            console.log("No se cumplió ningún caso");
            break;
    }

Como podemos ver, al no tener el break en el case : 10, se ejecuta el código tanto de ese caso , como del caso siguiente.

While

Ahora que hemos visto los condicionales pasemos a los ciclos.

Un ciclo es una estructura de control que nos permite ejecutar un mismo bloque de código varias veces hasta que cierta condición no sea cierta (conocida como condición de permanencia).

El termino iteración es un termino altamente asociado a los ciclos, pues suele referirse a cada ejecución del bloque de código indicada por el ciclo; es decir, a cada repetición.

La estructura de control while verifica una condición y ejecuta el bloque de código siempre que está condición sea cierta; en cuanto la condición sea falsa el ciclo dejará de ejecutarse.

Un ciclo while en general, se caracteriza por el manejo manual de los iteradores. Un iterador es un valor que utilizamos para controlar las repeticiones del ciclo (usualmente aparece en la condición de permanencia) y evitar quedarnos estancados es un solo valor; esto ocasionaría que el ciclo se ejecutará infinitamente, pues al no cambiar el valor en la condición no llegaríamos nunca a un punto en donde la condición sería falsa. Para lograr esto, debemos modificar el valor de dicho iterador en cada ejecución del bloque de código.

Veamos un ejemplo. Imprimiremos todos los números pares del 1 al 20.

let i = 0;
while(i <= 20){
    if(i % 2 == 0)
        console.log(i);
    i ++;
}

Para empezar, definimos la variable i con el valor 0. Utilizaremos esta variable como nuestro iterador; se encargará de recorrer cada número entero entre 0 y 20.

En el while definimos la clausula de permanencia como: mientras i sea menor o igual que 20.

Dentro del ciclo solo comprobamos si i es par, y de ser así, imprimimos su valor en la consola. Al final, se ejecute o no el if, incrementamos en uno el valor de i. De esta forma en cada iteración i cambiará su valor, con lo que, eventualmente, será mayor que 20, y la condición de permanencia dejará de cumplirse por lo que el ciclo terminará de ejecutarse.

Hablemos un poco de los ciclos infinitos.

Los ciclos infinitos son un error de programación en el que el ciclo nunca llega a un caso donde la condición de permanencia sea falsa, por lo que el ciclo siempre se ejecutará durante la ejecución del programa. Un ciclo infinito se puede ver tan fácil como el siguiente ejemplo:

let i = 1
while (i >= 0) {
    console.log("Se ejecuta el while");
}

Como podemos ver, lo único que ocurre en el cuerpo del while es que imprimimos el mensaje “Se ejecuta el while”, pero el valor de i nunca cambia. Entonces en cada iteración, al verificar la condición de permanencia, siempre tendremos que i = 1 >= 0. Esto provoca que el ciclo no se detenga nunca, por lo que continuaría ejecutándose infinitamente, lo que ocasionaría un error conocido como desbordamiento de memoria.

Do-while

El ciclo do-while funciona de forma muy similar al while, excepto por una pequeña diferencia.

Como podemos ver, en primer lugar definimos el bloque de código a ejecutar dentro del cuerpo del do y después verificamos la condición de permanencia; esto significa que el bloque de código se ejecutara por lo menos una vez, a diferencia del while que primero verifica la condición, por lo que puede haber casos en que el cuerpo del while no se ejecuta nunca.

Veamos un ejemplo:

let i = 15;
while(i < 10){
    console.log("Se ejecuto el while");
}

do{
    console.log("Se ejecuto el do-while");
}while(i < 10);

Como podemos ver, el cuerpo del while no se ejecuta, pues inmediatamente la condición es falsa; sin embargo, el cuerpo del do-while se ejecuta una vez, y es hasta que esa ejecución termina, que se checa la condición de permanencia y el ciclo termina.

For

De manera similar a while, el for es otra estructura de control que nos permite ejecutar un bloque código cierto número de veces.

Es posible realizar las mismas actividades utilizando cualquiera de los dos ciclos, sin embargo, es algunas situaciones es más sencillo utilizar uno o el otro.

La sintaxis del for se compone de tres elementos principales:

Iterador

Al igual que en el while, la estructura for utiliza un iterador para controlar el número de veces que se ejecuta un ciclo.

Comúnmente el iterador se crea como una nueva variable solo para usarse en el ciclo, sin embargo esto no es obligatorio y puede utilizase una variable que haya sido creada antes en alguna otra parte del código. Este sentencia se ejecuta una sola vez, al inicio de la ejecución del ciclo.

for (let i = 0; permanencia; incremento) {
    // Bloque de código.
}

let j;
for(j = 10; permanencia; incremento){
    //Bloque de código.
}
Permanencia

Aquí, también necesitamos una condición de permanencia que nos diga si continuar o no la ejecución del ciclo.

Si la condición se evalúa como true el cuerpo del ciclo se ejecuta por otra iteración, si la condición se evalúa como false la ejecución termina.

for (let i = 0; i < 10; incremento) {
    //Bloque de código  
}
Incremento

El tercer elemento es la modificación del valor del iterador. Es común que esta sentencia consista en incrementar el valor del iterador, pero también es posible decrementarlo.

for (let i = 0; i < 10; i += 2) {
    //Bloque de código  
}

En este caso, estamos incrementando el valor del iterador de 2 en 2.

Veamos un ejemplo:

for (let i = 0; i <= 20; i++) {
    if(i % 2 == 0)
        console.log(i);
}

Comenzamos por crear la variable i y asignarle el valor de 0. Después, al inicio de cada vuelta del ciclo verificamos si i es menor o igual que 20, de ser así, ejecutamos el cuerpo del for y finalmente incrementamos el valor de i en 1, lo contrario la ejecución del ciclo termina.

Como puedes ver, es el mismo ejemplo que utilizamos para el while, pues como mencionábamos arriba, es posible realizar las mismas tareas utilizando ambas estructuras.

Try-catch

La estructura de control try-catch no permite definir un bloque código para intentar ejecutar (try), y una respuesta si es que se produce un excepción en dicha ejecución (catch).

Si cuando se ejecuta el bloque de código del try alguna de las sentencias produce una excepción o error, inmediatamente se pasa a la ejecución del bloque de catch. Si no se lanza ninguna excepción, el bloque del catch no se ejecuta.

Veamos un ejemplo:

try{
    ejemplo();
}
catch(e){
    console.log("Ha ocurrido el error: " +  e);
}

//Ha ocurrido el error:ReferenceError: ejemplo is not defined

En este caso, intentamos llamar a una función ejemplo que no hemos creado, por lo que la excepción que atrapamos nos dice que ejemplo no está definida.