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.
Lareferencia 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. ConsultaExpresiones y operadores
para obtener información completa sobre las expresiones.
Declaración de bloque
La declaración más básica es unadeclaració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.
Nota: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, lasdeclaraciones 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:
js var x = 1; { var x = 2;}console.log(x); // muestra 2
Esto muestra2
porque la instrucciónvar x
dentro del bloque está en el mismo ámbito que la instrucciónvar x
anterior del bloque. (En C o Java, el código equivalente habría generado1
).
A partir de ECMAScript2015, las declaraciones de variableslet
yconst
tienen un ámbito de bloque. Consulta las páginas de referencia delet
yconst
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
yswitch
.
Expresiónif...else
Utiliza la expresiónif
para ejecutar una instrucción si una condición lógica estrue
. Utiliza la cláusula opcionalelse
para ejecutar una instrucción si la condición esfalse
.
Una declaraciónif
se ve así:
if (condition) { statement_1;} else { statement_2;}
Aquí, lacondition
puede ser cualquier expresión que se evalúe comotrue
ofalse
. (ConsultaBoolean
para obtener una explicación de lo que se evalúa comotrue
yfalse
).
Sicondition
se evalúa comotrue
, se ejecutastatement_1
. De lo contrario, se ejecutastatement_2
.statement_1
ystatement_2
pueden ser cualquier declaración, incluidas otras declaracionesif
anidadas.
También puedes componer las declaraciones usandoelse 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 comotrue
. 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 declaracionesif
:
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 comofalse
(también conocidos como valoresFalsy:
false
undefined
null
0
NaN
- la cadena vacía (
""
)
Todos los demás valores, incluidos todos los objetos, se evalúan comotrue
cuando se pasan a una declaración condicional.
Nota:¡No confundas los valores booleanos primitivostrue
yfalse
con los valorestrue
yfalse
del objetoBoolean
!.Por ejemplo:```jsvar b = new Boolean(false);if (b) // esta condición se evalúa como verdaderaif (b == true) // esta condición se evalúa como false
Ejemplo
En el siguiente ejemplo, la funcióncheckData
devuelvetrue
si el número de caracteres en un objetoText
es tres. De lo contrario, muestra una alerta y devuelvefalse
.
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ónswitch
Una instrucciónswitch
permite que un programa evalúe una expresión e intente hacer coincidir el valor de la expresión con una etiquetacase
. Si la encuentra, el programa ejecuta la declaración asociada.
Una instrucciónswitch
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ónswitch
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 deswitch
. - (Por convención, la cláusula
default
está escrita como la última cláusula, pero no es necesario que sea así).
- Si se encuentra una cláusula
Declaracionesbreak
La declaración opcionalbreak
asociada con cada cláusulacase
asegura que el programa salga deswitch
una vez que se ejecuta la instrucción coincidente, y luego continúa la ejecución en la declaración que sigue aswitch
. Si se omitebreak
, el programa continúa la ejecución dentro de la instrucciónswitch
(y evaluará el siguientecase
, y así sucesivamente).
Ejemplo
En el siguiente ejemplo, sifruittype
se evalúa como 'Bananas
', el programa hace coincidir el valor con el caso 'Bananas
' y ejecuta la declaración asociada. Cuando se encuentrabreak
, el programa sale delswitch
y continúa la ejecución de la instrucción que sigue aswitch
. Si se omitierabreak
, también se ejecutará la instrucción paracase '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ónthrow
y manejarlas usando las declaracionestry...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 interfazDOMException 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 interfazDOMError describe un objeto de error que contiene un nombre de error.
Expresiónthrow
Utiliza la expresiónthrow
para lanzar una excepción. Una expresiónthrow
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 Stringthrow 42; // tipo Numberthrow true; // tipo Booleanthrow { 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 bloquecatch
.
// Crea un objeto tipo de UserExceptionfunction 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íralathrow new UserException("Valor muy alto");
Declaracióntry...catch
La declaracióntry...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óntry...catch
la detecta.
La declaracióntry...catch
consta de un bloquetry
, que contiene una o más declaraciones, y un bloquecatch
, que contiene declaraciones que especifican qué hacer si se lanza una excepción en el bloquetry
.
En otras palabras, deseas que el bloquetry
tenga éxito, pero si no es así, deseas que el control pase al bloquecatch
. Si alguna instrucción dentro del bloquetry
(o en una función llamada desde dentro del bloquetry
) arroja una excepción, el controlinmediatamente cambia al bloquecatch
. Si no se lanza ninguna excepción en el bloquetry
, se omite el bloquecatch
. El bloquefinalmente
se ejecuta después de que se ejecutan los bloquestry
ycatch
, pero antes de las declaraciones que siguen a la declaracióntry...catch
.
El siguiente ejemplo usa una instruccióntry...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 bloquecatch
establezca la variablemonthName
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 bloquecatch
Puedes usar un bloquecatch
para manejar todas las excepciones que se puedan generar en el bloquetry
.
catch (catchID) { instrucciones}
El bloquecatch
especifica un identificador (catchID
en la sintaxis anterior) que contiene el valor especificado por la expresiónthrow
. Puedes usar este identificador para obtener información sobre la excepción que se lanzó.
JavaScript crea este identificador cuando se ingresa al bloquecatch
. El identificador dura solo la duración del bloquecatch
. Una vez que el bloquecatch
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 bloquecatch
.
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}
Nota:Cuando se registran errores en la consola dentro de un bloquecatch
, se usaconsole.error()
en lugar deconsole.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 bloquefinally
El bloquefinally
contiene instrucciones que se ejecutarándespués que se ejecuten los bloquestry
ycatch
. Además, el bloquefinally
ejecutaantes el código que sigue a la declaracióntry...catch...finally
.
También es importante notar que el bloquefinally
se ejecutaráindependientemente de que se produzca una excepción. Sin embargo, si se lanza una excepción, las declaraciones en el bloquefinally
se ejecutan incluso si ningún bloquecatch
maneje la excepción que se lanzó.
Puedes usar el bloquefinally
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 bloquefinally
cierra el archivo antes de que falle el script. Usarfinally
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 bloquefinally
devuelve un valor, este valor se convierte en el valor de retorno de toda la producción detry…catch…finally
, independientemente de las declaracionesreturn
en los bloquestry
ycatch
:
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 bloquefinally
también se aplica a las excepciones lanzadas o relanzadas dentro del bloquecatch
:
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
Declaracionestry...catch
anidadas
Puedes anidar una o más declaracionestry...catch
.
Si un bloquetry
internono tiene un bloquecatch
correspondiente:
- debe contener un bloque
finally
, y - el bloque
catch
adjunto de la declaracióntry...catch
se comprueba para una coincidencia.
Para obtener más información, consultabloques try anidados
en la una página de referenciatry...catch
.
Utilizar objetosError
Dependiendo del tipo de error, es posible que puedas utilizar las propiedadesname
ymessage
para obtener un mensaje más refinado.
La propiedadname
proporciona la clase general deError
(tal comoDOMException
oError
), mientras quemessage
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 bloquecatch
no discrimina entre tus propias excepciones y las del sistema), puedes usar el constructorError
.
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}