Control de flujo y manejo de errores

JavaScript admite un compacto conjunto de declaraciones, espec铆ficamente declaraciones de control de flujo, que puedes utilizar para incorporar una gran cantidad de interactividad en tu aplicaci贸n. Este cap铆tulo proporciona una descripci贸n de estas declaraciones.

La referencia de JavaScript contiene detalles exhaustivos sobre las declaraciones de este cap铆tulo. El car谩cter de punto y coma (;) se utiliza para separar declaraciones en c贸digo JavaScript.

Todas las expresiones e instrucciones de JavaScript tambi茅n son una declaraci贸n. Consulta Expresiones y operadores para obtener informaci贸n completa sobre las expresiones.

Declaraci贸n de bloque

La declaraci贸n m谩s b谩sica es una declaraci贸n de bloque, que se utiliza para agrupar instrucciones. El bloque est谩 delimitado por un par de llaves:

{
  statement_1;
  statement_2;
  鈰
  statement_n;
}

Ejemplo

Las declaraciones de bloque se utilizan com煤nmente con declaraciones de control de flujo (if, for, while).

while (x < 10) {
  x++;
}

Aqu铆, { x++; } es la declaraci贸n de bloque.

Importante: JavaScript anterior a ECMAScript2015 (6a edici贸n) no tiene 谩mbito de bloque. En JavaScript m谩s antiguo, las variables introducidas dentro de un bloque tienen como 谩mbito la funci贸n o script que las contiene, y los efectos de establecerlas persisten m谩s all谩 del bloque en s铆 mismo. En otras palabras, las declaraciones de bloque no definen un 谩mbito.

Los bloques "independientes" en JavaScript pueden producir resultados completamente diferentes de los que producir铆an en C o Java. Por ejemplo:

var x = 1;
{
  var x = 2;
}
console.log(x); // muestra 2

Esto muestra 2 porque la instrucci贸n var x dentro del bloque est谩 en el mismo 谩mbito que la instrucci贸n var x anterior del bloque. (En C o Java, el c贸digo equivalente habr铆a generado 1).

A partir de ECMAScript2015, las declaraciones de variables let y const tienen un 谩mbito de bloque. Consulta las p谩ginas de referencia de let y const para obtener m谩s informaci贸n.

Expresiones condicionales

Una expresi贸n condicional es un conjunto de instrucciones que se ejecutar谩n si una condici贸n especificada es verdadera. JavaScript admite dos expresiones condicionales: if...else y switch.

Expresi贸n if...else

Utiliza la expresi贸n if para ejecutar una instrucci贸n si una condici贸n l贸gica es true. Utiliza la cl谩usula opcional else para ejecutar una instrucci贸n si la condici贸n es false.

Una declaraci贸n if se ve as铆:

if (condition) {
  statement_1;
} else {
  statement_2;
}

Aqu铆, la condition puede ser cualquier expresi贸n que se eval煤e como true o false. (Consulta Boolean para obtener una explicaci贸n de lo que se eval煤a como true y false).

Si condition se eval煤a como true, se ejecuta statement_1. De lo contrario, se ejecuta statement_2. statement_1 y statement_2 pueden ser cualquier declaraci贸n, incluidas otras declaraciones if anidadas.

Tambi茅n puedes componer las declaraciones usando else if para que se prueben varias condiciones en secuencia, de la siguiente manera:

if (condition_1) {
  statement_1;
} else if (condition_2) {
  statement_2;
} else if (condition_n) {
  statement_n;
} else {
  statement_last;
}

En el caso de m煤ltiples condiciones, solo se ejecutar谩 la primera condici贸n l贸gica que se eval煤e como true. Para ejecutar m煤ltiples declaraciones, agr煤palas dentro de una declaraci贸n de bloque ({ 鈥 }).

Mejores pr谩cticas

En general, es una buena pr谩ctica usar siempre declaraciones de bloque, especialmente al anidar declaraciones if:

if (condition) {
  statement_1_runs_if_condition_is_true;
  statement_2_runs_if_condition_is_true;
} else {
  statement_3_runs_if_condition_is_false;
  statement_4_runs_if_condition_is_false;
}

No es aconsejable utilizar asignaciones simples en una expresi贸n condicional, porque la asignaci贸n se puede confundir con la igualdad al mirar el c贸digo.

Por ejemplo, no escribas un c贸digo como este:

// Propenso a ser mal interpretado como "x == y"
if (x = y) {
  /* expresiones aqu铆 */
}

Si necesitas usar una tarea en una expresi贸n condicional, una pr谩ctica com煤n es poner par茅ntesis adicionales alrededor de la asignaci贸n, as铆:

if ((x = y)) {
  /* expresiones aqu铆 */
}

Valores falsos

Los siguientes valores se eval煤an como false (tambi茅n conocidos como valores Falsy:

  • false
  • undefined
  • null
  • 0
  • NaN
  • la cadena vac铆a ("")

Todos los dem谩s valores, incluidos todos los objetos, se eval煤an como true cuando se pasan a una declaraci贸n condicional.

Precauci贸n: 隆No confundas los valores booleanos primitivos true y false con los valores true y false del objeto Boolean!.

Por ejemplo:

var b = new Boolean(false);
if (b)         // esta condici贸n se eval煤a como verdadera
if (b == true) // esta condici贸n se eval煤a como false

Ejemplo

En el siguiente ejemplo, la funci贸n checkData devuelve true si el n煤mero de caracteres en un objeto Text es tres. De lo contrario, muestra una alerta y devuelve false.

function checkData() {
  if (document.form1.threeChar.value.length == 3) {
    return true;
  } else {
    alert(
        'Introduce exactamente tres caracteres. ' +
        `${document.form1.threeChar.value} no es v谩lido.`);
    return false;
  }
}

Declaraci贸n switch

Una instrucci贸n switch permite que un programa eval煤e una expresi贸n e intente hacer coincidir el valor de la expresi贸n con una etiqueta case. Si la encuentra, el programa ejecuta la declaraci贸n asociada.

Una instrucci贸n switch se ve as铆:

switch (expression) {
  case label_1:
    statements_1
    [break;]
  case label_2:
    statements_2
    [break;]
    鈥
  default:
    statements_def
    [break;]
}

JavaScript eval煤a la instrucci贸n switch anterior de la siguiente manera:

  • El programa primero busca una cl谩usula case con una etiqueta que coincida con el valor de expresi贸n y luego transfiere el control a esa cl谩usula, ejecutando las declaraciones asociadas.
  • Si no se encuentra una etiqueta coincidente, el programa busca la cl谩usula opcional default:
    • Si se encuentra una cl谩usula default, el programa transfiere el control a esa cl谩usula, ejecutando las declaraciones asociadas.
    • Si no se encuentra una cl谩usula default, el programa reanuda la ejecuci贸n en la declaraci贸n que sigue al final de switch.
    • (Por convenci贸n, la cl谩usula default est谩 escrita como la 煤ltima cl谩usula, pero no es necesario que sea as铆).

Declaraciones break

La declaraci贸n opcional break asociada con cada cl谩usula case asegura que el programa salga de switch una vez que se ejecuta la instrucci贸n coincidente, y luego contin煤a la ejecuci贸n en la declaraci贸n que sigue a switch. Si se omite break, el programa contin煤a la ejecuci贸n dentro de la instrucci贸n switch (y evaluar谩 el siguiente case, y as铆 sucesivamente).

Ejemplo

En el siguiente ejemplo, si fruittype se eval煤a como 'Bananas', el programa hace coincidir el valor con el caso 'Bananas' y ejecuta la declaraci贸n asociada. Cuando se encuentra break, el programa sale del switch y contin煤a la ejecuci贸n de la instrucci贸n que sigue a switch. Si se omitiera break, tambi茅n se ejecutar谩 la instrucci贸n para case 'Cherries'.

switch (fruittype) {
  case 'Oranges':
    console.log('Las naranjas cuestan $0.59 la libra.');
    break;
  case 'Apples':
    console.log('Las manzanas cuestan $0.32 la libra.');
    break;
  case 'Bananas':
    console.log('Los pl谩tanos cuestan $0.48 la libra.');
    break;
  case 'Cherries':
    console.log('Las cerezas cuestan $3.00 la libra.');
    break;
  case 'Mangoes':
    console.log('Los mangos cuestan $0.56 la libra.');
    break;
  case 'Papayas':
    console.log('Los mangos y las papayas cuestan $2.79 la libra.');
    break;
  default:
   console.log(`Lo sentimos, no tenemos ${fruittype}.`);
}
console.log("驴Hay algo m谩s que quieras?");

Expresiones de manejo de excepciones

Puedes lanzar excepciones usando la instrucci贸n throw y manejarlas usando las declaraciones try...catch.

Tipos de excepciones

Casi cualquier objeto se puede lanzar en JavaScript. Sin embargo, no todos los objetos lanzados son iguales. Si bien es com煤n lanzar n煤meros o cadenas como errores, con frecuencia es m谩s efectivo usar uno de los tipos de excepci贸n creados espec铆ficamente para este prop贸sito:

  • excepciones ECMAScript
  • La interfaz DOMException representa un evento anormal (llamado excepci贸n) que ocurre como resultado de llamar a un m茅todo o acceder a una propiedad de una API web y la interfaz DOMError describe un objeto de error que contiene un nombre de error.

Expresi贸n throw

Utiliza la expresi贸n throw para lanzar una excepci贸n. Una expresi贸n throw especifica el valor que se lanzar谩:

throw expression;

Puedes lanzar cualquier expresi贸n, no solo expresiones de un tipo espec铆fico. El siguiente c贸digo arroja varias excepciones de distintos tipos:

throw 'Error2';   // tipo String
throw 42;         // tipo Number
throw true;       // tipo Boolean
throw {toString: function() { return "隆Soy un objeto!"; } };

Nota Puedes especificar un objeto cuando lanzas una excepci贸n. A continuaci贸n, puedes hacer referencia a las propiedades del objeto en el bloque catch.

// Crea un objeto tipo de UserException
function UserException(message) {
  this.message = message;
  this.name = 'UserException';
}

// Hacer que la excepci贸n se convierta en una bonita cadena cuando se usa como cadena
// (por ejemplo, por la consola de errores)
UserException.prototype.toString = function() {
  return `${this.name}: "${this.message}"`;
}

// Crea una instancia del tipo de objeto y t铆rala
throw new UserException('Valor muy alto');

Declaraci贸n try...catch

La declaraci贸n try...catch marca un bloque de expresiones para probar y especifica una o m谩s respuestas en caso de que se produzca una excepci贸n. Si se lanza una excepci贸n, la declaraci贸n try...catch la detecta.

La declaraci贸n try...catch consta de un bloque try, que contiene una o m谩s declaraciones, y un bloque catch, que contiene declaraciones que especifican qu茅 hacer si se lanza una excepci贸n en el bloque try.

En otras palabras, deseas que el bloque try tenga 茅xito, pero si no es as铆, deseas que el control pase al bloque catch. Si alguna instrucci贸n dentro del bloque try (o en una funci贸n llamada desde dentro del bloque try) arroja una excepci贸n, el control inmediatamente cambia al bloque catch. Si no se lanza ninguna excepci贸n en el bloque try, se omite el bloque catch. El bloque finalmente se ejecuta despu茅s de que se ejecutan los bloques try y catch, pero antes de las declaraciones que siguen a la declaraci贸n try...catch.

El siguiente ejemplo usa una instrucci贸n try...catch. El ejemplo llama a una funci贸n que recupera el nombre de un mes de un arreglo en funci贸n del valor pasado a la funci贸n. Si el valor no corresponde a un n煤mero de mes (1-12), se lanza una excepci贸n con el valor "InvalidMonthNo" y las declaraciones en el bloque catch establezca la variable monthName en 'unknown'.

function getMonthName(mo) {
  mo = mo - 1; // Ajusta el n煤mero de mes para el 铆ndice del arreglo (1 = Ene, 12 = Dic)
  let months = ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul',
                'Ago', 'Sep', 'Oct', 'Nov', 'Dic'];
  if (months[mo]) {
    return months[mo];
  } else {
    throw 'InvalidMonthNo'; // aqu铆 se usa la palabra clave throw
  }
}

try { // declaraciones para try
  monthName = getMonthName(myMonth); // la funci贸n podr铆a lanzar una excepci贸n
}
catch (e) {
  monthName = 'unknown';
  logMyErrors(e); // pasar el objeto exception al controlador de errores (es decir, su propia funci贸n)
}

El bloque catch

Puedes usar un bloque catch para manejar todas las excepciones que se puedan generar en el bloque try.

catch (catchID) {
  instrucciones
}

El bloque catch especifica un identificador (catchID en la sintaxis anterior) que contiene el valor especificado por la expresi贸n throw. Puedes usar este identificador para obtener informaci贸n sobre la excepci贸n que se lanz贸.

JavaScript crea este identificador cuando se ingresa al bloque catch. El identificador dura solo la duraci贸n del bloque catch. Una vez que el bloque catch termina de ejecutarse, el identificador ya no existe.

Por ejemplo, el siguiente c贸digo lanza una excepci贸n. Cuando ocurre la excepci贸n, el control se transfiere al bloque catch.

try {
  throw 'myException'; // genera una excepci贸n
}
catch (err) {
  // declaraciones para manejar cualquier excepci贸n
  logMyErrors(err);    // pasa el objeto exception al controlador de errores
}

Mejores pr谩cticas: Cuando se registran errores en la consola dentro de un bloque catch, se usa console.error() en lugar de console.log() aconsejado para la depuraci贸n. Formatea el mensaje como un error y lo agrega a la lista de mensajes de error generados por la p谩gina.

El bloque finally

El bloque finally contiene instrucciones que se ejecutar谩n despu茅s que se ejecuten los bloques try y catch. Adem谩s, el bloque finally ejecuta antes el c贸digo que sigue a la declaraci贸n try...catch...finally.

Tambi茅n es importante notar que el bloque finally se ejecutar谩 independientemente de que se produzca una excepci贸n. Sin embargo, si se lanza una excepci贸n, las declaraciones en el bloque finally se ejecutan incluso si ning煤n bloque catch maneje la excepci贸n que se lanz贸.

Puedes usar el bloque finally para hacer que tu script falle correctamente cuando ocurra una excepci贸n. Por ejemplo, es posible que debas liberar un recurso que tu script haya inmovilizado.

El siguiente ejemplo abre un archivo y luego ejecuta declaraciones que usan el archivo. (JavaScript de lado del servidor te permite acceder a los archivos). Si se lanza una excepci贸n mientras el archivo est谩 abierto, el bloque finally cierra el archivo antes de que falle el script. Usar finally aqu铆 asegura que el archivo nunca se deje abierto, incluso si ocurre un error.

openMyFile();
try {
  writeMyFile(theData); // Esto puede arrojar un error
} catch(e) {
  handleError(e); // Si ocurri贸 un error, man茅jalo
} finally {
  closeMyFile(); // Siempre cierra el recurso
}

Si el bloque finally devuelve un valor, este valor se convierte en el valor de retorno de toda la producci贸n de try鈥atch鈥inally, independientemente de las declaraciones return en los bloques try y catch:

function f() {
  try {
    console.log(0);
    throw 'bogus';
  } catch(e) {
    console.log(1);
    return true;    // esta declaraci贸n de retorno est谩 suspendida
                    // hasta que el bloque finally se haya completado
    console.log(2); // no alcanzable
  } finally {
    console.log(3);
    return false;   // sobrescribe el "return" anterior
    console.log(4); // no alcanzable
  }
  // "return false" se ejecuta ahora
  console.log(5);   // inalcanzable
}
console.log(f()); // 0, 1, 3, false

La sobrescritura de los valores devueltos por el bloque finally tambi茅n se aplica a las excepciones lanzadas o relanzadas dentro del bloque catch:

function f() {
  try {
    throw 'bogus';
  } catch(e) {
    console.log('captura "falso" interno');
    throw e; // esta instrucci贸n throw se suspende hasta
             // que el bloque finally se haya completado
  } finally {
    return false; // sobrescribe el "throw" anterior
  }
  // "return false" se ejecuta ahora
}

try {
  console.log(f());
} catch(e) {
  // 隆esto nunca se alcanza!
  // mientras se ejecuta f(), el bloque `finally` devuelve false,
  // que sobrescribe el `throw` dentro del `catch` anterior
  console.log('"falso" externo capturado');
}

// Produce
// "falso" interno capturado
// false

Declaraciones try...catch anidadas

Puedes anidar una o m谩s declaraciones try...catch.

Si un bloque try interno no tiene un bloque catch correspondiente:

  1. debe contener un bloque finally, y
  2. el bloque catch adjunto de la declaraci贸n try...catch se comprueba para una coincidencia.

Para obtener m谩s informaci贸n, consulta bloques try anidados en la una p谩gina de referencia try...catch.

Utilizar objetos Error

Dependiendo del tipo de error, es posible que puedas utilizar las propiedades name y message para obtener un mensaje m谩s refinado.

La propiedad name proporciona la clase general de Error (tal como DOMException o Error), mientras que message generalmente proporciona un mensaje m谩s conciso que el que se obtendr铆a al convertir el objeto error en una cadena.

Si est谩s lanzando tus propias excepciones, para aprovechar estas propiedades (por ejemplo, si tu bloque catch no discrimina entre tus propias excepciones y las del sistema), puedes usar el constructor Error.

Por ejemplo:

function doSomethingErrorProne() {
  if (ourCodeMakesAMistake()) {
    throw (new Error('El mensaje'));
  } else {
    doSomethingToGetAJavascriptError();
  }
}try {
  doSomethingErrorProne();
} catch (e) {               // AHORA, en realidad usamos `console.error()`
  console.error(e.name);    // registra 'Error'
  console.error(e.message); // registra 'The message' o un mensaje de error de JavaScript
}