Si las expresiones en JS son frases. Los estamentos en JS son sentencias u ordenes. Tal como en nuestro lenguaje las sentencias son separadas unas de otros o terminadas, con un punto. Los estamentos en JS son terminados con punto y coma ;
. Las expresiones son evaluadas para producir un valor, pero los estamentos son ejecutados para hacer que algo ocurra.
Una de las maneras de hacer que algo ocurra es evaluar una expresión que tiene efectos secundarios. Las expresiones con efectos sencundarios, tales como las asignaciones o las invocaciones, son por si mismas estamentos, y cuando son usadas de esa manera se las conoce como expresiones estamento. En una categoría parecida están los estamentos declarativos que declaran nuevas variables y definen nuevas funciones.
Los programas escritos en JS no son más que una secuencia de estamentos a ejecutar. Por defecto, el intérprete de JS ejecuta dichos estamentos uno detrás del otro en el orden que fueron escritos.
Otra manera de hacer que algo suceda, es alterar el orden de ejecución y JS tiene algunos estamentos o estructuras de control para justo esa acción:
- Condicionales: estamentos como
if
oswitch
hacen que el intérprete de JS ejecute o salte algunos estamentos dependiendo del valor de una expresión. - Loops: estamentos como
while
ofor
que ejecutar otros estamentos de manera repetida. - Saltos: estamentos como
break
,return
ythrow
que hacen que el interprete salte de una parte a otra del programa.
La forma más simple de estamentos en JS son las expresiones que tienen efectos secundarios:
greetings = "Hello " + name;
i *= 3;
A su vez los operadores de incremento y decremento (++
, --
) son también estamentos de asignación por su naturaleza.
counter++;
Ciertas llamadas a funciones que producen efectos secundarios son también estamentos de expresión:
displaySpinner();
const x = Math.cos(y);
TODO: IMPLEMENTAR
Los estamentos de condicionales ejecutan o saltan otros estamentos dependiendo del valor de una expresión específica. Estos estamentos son puntos de decisión en tu código, y son conocidos como ramificaciones. Si imaginamos al intérprete de JS siguiendo un camino a través del código, los condicionales serían lugares donde el código se ramifica en dos o más caminos y el intérprete debe elegir cual de ellos seguir.
El estamento if
es el estamento de control fundamental que permite a JS tomar decisiones, o más preciso, ejecutar estamentos condicionalmente. Tiene dos formas, la primera:
if (expresión)
estamento
En esta forma, la expresión es evaliada. Si el resultado de la evaluación es un valor verdadero, el estamento es ejecutado. Si el resultado de la evaluación es falso, entonces no se ejecuta. Por ejemplo:
if (username == null) {
username = "Iván Zamarro";
}
// o su equivalente
if (username == null) username = "Iván Zamarro";
Los parentesis alrededor de la expresión a evaluar, son requeridos y son parte de la sintáxis de if
.
Aunque JS requiere en sus if
al menos un estamento, podemos usar un bloque de estamento para combinar múltiples en uno.
if (!address) {
address = "";
message = "Escribe una dirección de email";
}
La segunda forma de los if
introduce una cláusula else
que es ejecutada cuando la expresión evalua a false
:
if (expresión)
estamento1
else
estamento2
Esta forma de estamento ejecuta el primer estamento si la expresión es verdadera y la segunda si es falsa:
if (n === 1) console.log("Tienes un nuevo mensaje!");
else console.log(`Tienes ${n} nuevos mensajes!`);
Si tenemos múltiples if
anidados con cláusulas else
, hay que poner cuidado en asegurarse de quenuestro else
va con el if
apropiado:
if (i === j) {
if (j === k) console.log("i es igual a k");
else console.log("i no es igual a j");
}
La regla en JS es que por defecto un else
es parte de su if
más cercano. En general para mantener los estamentos if
mejor ordenados, legibles y debugeables, es mejor usar llaves:
if (i === j) {
if (j === k) {
console.log("i es igual a k");
}
} else {
console.log("i no es igual a j");
}
El estamento if/else
evalua la expresión y ejecuta una o dos piezas de código, dependiendo de su valor. Pero ¿qué pasa cuando necesitamos ejecutar una o más piezas de código? Una forma de hacerlo es con un estamento else if
. else if
no es en realidad un estamento de JS, sino algo de azúcar sintáctico para aquellas veces en las que debemos repetir estamentos if/else
:
if (n === 1) {
// pasan cosas
} else if (n === 2) {
// pasan más cosas
} else if (n === 3) {
// pues otras cosas pasan
} else {
// si todo lo anterior falla, pasan cosas aquí
}
No tiene nada especial este código. Solo es una serie de estamentos if
, en el que cada if
es parte de una cláusula else
del estamento previo. Esto hace más legible el código, lo siguiente sería equivalente:
if (n === 1) {
// pasan cosas
} else {
if (n === 2) {
// pasan más cosas
} else {
if (n === 3) {
// pues otras cosas pasan
} else {
// si todo lo anterior falla, pasan cosas aquí
}
}
}
Un estamento if
causa una ramificación en el flujo de ejecución de nuestros programas, y podemos usar su forma else if
para realizar una multiramificación. Aunque esta no es la mejor solución cuando nuestrar ramas dependen del valor de la misma expresión. En estos casos, es un gasto de tiempo evaluar la misma expresión en múltiples if
.
El estamento switch
maneja justo esta situación. La palabra clave switch
es seguida de una expresión entre paréntesis y un bloque de llaves:
switch (expresión) {
estamentos
}
Aunque su sintáxis es algo más complicada que lo anterior. En el bloque entre llaves varias variaciones son etiquetadas con la palabra clave case
seguida de una expresión y dos puntos :
. Cuando un switch
se ejecuta, calcula el valor de la expresión y luego busca el case
cuya expresión evalua al mismo valor (donde la igualdad la determina el operador ===
). Si encuentra una ejecuta el bloque de código del estamento etiquetado para ese case
. Sino encuentra ningún case
que coincida con dicho valor, ejecutara el estamento etiquetado como default:
. Si no existe un default
el switch
sera saltado completamente.
switch
es dificil de explicar, asi que refactorizemos el anterior if/else
a un switch
:
switch (n) {
case 1: // empieza aqui si n === 1
// pasan cosas
break; // para aquí
case 2:
// pasan más cosas
break;
case 3:
// pues otras cosas pasan
break;
default:
// si todo lo anterior falla, pasan cosas aquí
break;
}
La palabra break
usada al final de cada case
hace que el intérprete salte al final del estamento switch
y continue con el siguiente. Cada case
en un switch
especifica solamente el punto de entrada del código deseado a ejecutar, no un punto de salida. Si no hay un estamento de salto break
, entonces switch
empieza a ejecutar primero el estamento que coincide con el valor de la expresión, pero luego continuaria ejecutando estamentos hasta que alcanzara el final del bloque. Aunque aveces este comportamiento de caer de un case
a otro es útil, el 99% de las veces debemos preocuparnos de salir del case
, bien sea con un break
o un return
.
function convert(x) {
switch (typeof x) {
case "number":
return x.toString(16);
case "string":
return `"${x}"`;
default:
return String(x);
}
}
Aunque normalmente usaremos los case
seguidos de literales de números y strings, JS permite que cada case
sea seguido de una expresión cualquiera (aunque esto es poco útil).
El estamento switch
primero evalua la expresión que sigue a su palabra clave y luego cada expresión de case
hasta que encuentra la que coincide. La coincidencia es determinada usando el operador de igualdad estricta ===
por lo que no habrá conversión de tipos.
Como no todas las expresiones case
son evaluadas cada vez que el estamento switch
es ejecutado, debes evitar usar expresiones en case
que tengan efectos secundarios como asignaciones o llamadas a funciones.
Si ninguno de los case
coincide con la expresión evaluada del switch
, se ejecutará el cuerpo del estamento etiquetado como default
. Si no hay un default
entonces se saltará todo el cuerpo del switch. Aunque default
puede aparecer en cualquier lado del switch
lo lógico es establecerlo como último estamento después de todos los case
.
Nota: Puedes agrupar estamentos case
con las mismas condiciones
switch (val) {
case 1:
case 2:
case 3:
result = "1, 2, or 3";
break;
case 4:
result = "4 alone";
}
Para entender los estamentos condicionales, hemos imaginado al intérprete de js siguiendo un camino ramificado a través de nuestro código. Los estamentos de loop son aquellos que doblan dicho camino sobre si mismo para repetir porciones del código. JS tiene 5 tipos de loops: while
, do/while
, for
, for/of
-(for/await
), for/in
. Un uso muy común de ellos es iterar sobre elementos de un array.
El estamento while
es el loop básico de JS. Tiene la siguiente sintáxis:
while(expression)
estamento
Para ejecutar el estamento while
el intérprete primero evalua la expresión. Si el valor de la expresión es falso, entonces el intérprete salta sobre dicho estamento y pasa al siguiente sin ejecutar el cuerpo del while
. Si es cierto, el intérprete ejecutara el estamento y repetira, evaluando la expresión de nuevo. Es decir, ejecutará de manera repetida el estamento mientras la expresión sea cierta. Con lo cual podemos crear loops infinitos con while(true)
.
Normalmente no queremos que JS repita la misma operación de manera continua. En casi todos los loop, una o más variables cambian en cada iteración. En el momento en el que las variables cambian, cada acción ejecutada en el cuerpo del loop, el estamento, puede diferir en cada iteración. Iendo más lejos, si el cambio de variable o variables involucra a la expresión del while
, el valor de la expresión diferira en cada iteración. Esto es importante, puesde otra manera una expresión que comienza cierta podria no cambiar nunca y el loop no terminaría.
let count = 0;
while (count < 10) {
console.log(count);
count++;
}
TODO: Implementar
El estamento for
provee un constructor de loop que suele ser mas conveniente que el de while
. for
simplifica los loops que sigen un patrón común. Muchos loops tienen una variable contador de algún tipo. Esta variable es inicializada antes de que empieze el loop y testeada antes de cada iteración del loop. Finalmente, la variable contador es incrementada o actualizada al final cuerpo del loop, justo antes de que la variable sea testeada de nuevo. En este tipo de loops, la inicialización, el testeo y la actualización son las tres manipulaciones cruciales sobre la variable del loop. El estamento for
enclausula estas tres manipulaciones como expresiones y hace de esas expresiones parte de la sintaxis del loop:
for (inicializacion; test; incremento)
estamento
La inicialización, testeo e incremento son tres expresiones, separadas por ;
que son responsables de manipular la variable del loop. Poniendolas todas al inicio de la primera línea del loop se hace más fácil de entender que hace el for
y previene errores como evitar inicializar o incrementar la variable del loop.
A nivel del switch sería como:
inicializacion;
while(test) {
estamento;
incremento;
}
En otras palabras, la expresión de inicialización es evaluada una vez, antes de que el loop empieze. Para ser útil, esta expresión debe tener efectos secundarios, normalmente una asignación. JS también permite que la inicialización sea la declaración de una variable de tal manera que puedes declarar e inicializar la variable del loop al mismo tiempo. La expresión de testeo es evaluada antes de cada iteración y controla cuando el cuerpo del loop debe ser ejecutado. Sólo será ejecutado si el test evalua a un valor cierto. Finalmente la expresión de incremento es evaluada, debe ser una vez más una expresión con efectos secundarios para ser útil. Normalmente se usa una expresión de asignación, usando los operadores ++
o --
.
// imprime los numeros del 0 al nueve
for (let count = 0; count < 10; count++) {
console.log(count);
}
Los loops pueden ser mucho más complejos que el anterior ejemplo, y hay veces que más de una variable cambia en cada iteración. Esta situación es el único ligar donde el operador por coma es muy usado en JS, provee una manera de combinar múltiples inicializaciones y expresiones de incremento en una sola expresión capaz de usarse en un loop for
:
let i,
j,
sum = 0;
for (i = 0, j = 10; i < 10; i++, j--) {
sum += i * j;
}
Aunque normalmente la variable de un loop es numérica. No es necesario. A continuación vemos un loop for
que itera una lista linkeada y retorna el último objeto de la lista (el primero que tiene la propiedad next
):
function tail(o) {
for (; o.next != null; o = o.next) {
// solo itera mientras o.next exista
return o;
}
}
El código anterior no tiene expresión de inicialización. Cualquiera de las tres expresiones comentadas pueden ser omitidas en un loop for
, pero los ;
son requeridos. Si omitimos la expresión de test, el loop se repetirá para siempre, for(;;)
es equivalente a un while(true)
La recursividad es el concepto de que una función se puede expresar en términos de sí misma. Para ayudar a comprender esto, piensa en la siguiente tarea: multiplica los primeros n elementos de un array para crear el producto de esos elementos. Usando un bucle for
, puedes hacer esto:
function multiply(arr, n) {
let product = 1;
for (var i = 0; i < n; i++) {
product *= arr[i];
}
return product;
}
Sin embargo, si observas multiply(arr, n) == multiply(arr, n - 1) * arr[n - 1]
. Esto significa que podemos reescribir esta función para no necesitar un loop:
function multiply(arr, n) {
if (n <= 0) {
return 1;
} else {
return multiply(arr, n - 1) * arr[n - 1];
}
}
La versión recursiva de multiply
se descompone así. En el caso base, donde n <= 0
, devuelve 1. Para valores más grandes de n, se llama a sí mismo, pero con n - 1
. Esa llamada de función se evalúa de la misma manera, llamando a multiolay
nuevamente hasta que n <= 0
. En este punto, todas las funciones pueden retornar y la multiply
original devuelve la respuesta.
Nota: Las funciones recursivas deben tener un caso base cuando regresan sin llamar a la función nuevamente (en este ejemplo, cuando n <= 0
), de lo contrario nunca podrán terminar de ejecutarse.
Lee: El siguiente enlace
ES6 define un nuevo estamento de loop for/of
. Este estamento es completamente diferente al loop for
común.
El loop for/of
trabaja con objetos iterables. Arrays, strings, sets y maps son iterables: representan secuencias de conjuntos de elementos sobre los que puedes iterar usando un loop for/of
.
Por ejemplo, para iterar sobre los elementos de un array de números y calcular su suma:
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let sum = 0;
for (let element of data) {
sum += element;
}
sum; // => 45
La sintáxis es parecida a la de un loop for
: la palabra for
seguida de paréntesis que contienen detalles sobre qué debe hacer el loop. En este caso, los paréntesis contienen una declaración de variables (al igual que un loop for
pueden ser variables ya declaradas, usaríamos simplemente su nombre) seguido de la palabra of
y una expresión que debe evaluar a un objeto iterable, como data
en este caso. El cuerpo del loop for/of
sigue a los paréntesis entre llaves, como en cualquier otro loop.
En el anterior código, el cuerpo del loop es ejecutado por cada elemento del array data
. Antes de cada ejecución del cuerpo del loop, el siguiente elemento del array es asignado a la variable. Los elementos del array son iterados en orden de principio hasta el final.
Los arrays son iterados en vivo, cualquier cambio realizado durante la iteración afectará al resultado Si moficamos el anterior código y añadimos la línea data.push(sum);
en el cuerpo del loop, entonces crearemos un loop infinito porque nunca alcanzamos el final del array.
Los objetos no son iterables por defecto. Intentar usar un for/of
con un objeto lanzará un TypeError
. Si deseamos iterar las propiedades de un objeto, podemos usar un loop for/in
o mezclar for/of
con Object.keys()
:
const o = { x: 1, y: 2, z: 3 };
let sum = 0;
for (let k of Object.keys(o)) {
sum += o[k];
}
sum; // => 6
Esto funciona porque Object.keys()
retorna un array con los nombres de las propiedades del objeto, y los arrays siempre serán iterables por un bucle for/of
. Destacar que en este caso los cambios sobre el objeto, no afectarán a la ejecución del loop.
El anterior ejemplo puede ser escrito así, solo consultando valores:
const o = { x: 1, y: 2, z: 3 };
let sum = 0;
for (let v of Object.values(o)) {
sum += v;
}
sum; // => 6
Y si nos interesan ambos, claves y valores, podemos usar Object.entries()
con la asignación por desestructuración:
const o = { x: 1, y: 2, z: 3 };
let pairs = "";
for (let [k, v] of Object.entries(o)) {
pairs += k + v;
}
pairs; // => x1y2z3
TODO: IMPLEMENTAR
TODO: IMPLEMENTAR
Un bucle for/in
trabaja con cualquier objeto despues del in
.
El estamento for/in
itera a través de los nombres de propiedades de un objeto específico.
for (variable in object)
estamento
variable
suele referirse al nombre de una variable, pero puede ser la declaración de una variable o cualquier cosa configurable como el lado izquierdo de una expresión de asignación. object
es una expresión que debe evaluar a un objeto. Y como siempre estamento
es el blo que que sirve como cuerpo del loop:
for (let p in o) {
console.log(o[p]);
}
Para ejecutar un estamento for/in
, el intérprete de JS primero evalua la expresión de objeto. Si esta es null
o undefined
, el intérprete saltará el loop completamente. Al ejecutar el cuerpo del loop, lo hará una vez por cada propiedad enumerable del objeto. Antes de cada iteración, el intérprete evaluará la expresión variable
y asignará el nombre de la propiedad, un string, a ella.
Destacara que la variable
en un loop for/in
puede ser una expresión cualquiera, mientras evalue a algo que puede asignarse. Esta expresión es evaluada vada vez que iteramos el loop, lo que signifíca que puede ser diferente cada vez. Por ejemplo, para copiar todos los nombres de propiedades de un objeto en un array:
const o = {x: 1, y: 2, z: 3}
const a = [];
let i = 0;
for (a[i++] in o)
Aunque este tipo de bucles se puede usar con arrays, es preferible no hacerlo.
Hay que tener en cuenta que el bucle for/in
no siempre enumera todas las propiedades de un objeto. Por ejemplo esto no ocurrirá si estas son Symbol
. Y para aquellas propiedades cuyos nombres son strings, solo lo hará si son enumerables. Muchos métodos definidos en el core de JS no son enumerables. Todos los objetos tienen un método toString()
pero for/in
no los enumerará. Como regla general, todas las propiedades y métodos definidos por nuestro código son enumerables por defecto, pero no está garantizado que en los objetos de js esto se cumpla.
Las propiedades enumerables heredadas también serán enumeradas por el loop for/in
. Esto significa que usando un for/in
en código que hace uso de propiedades heredadas por todos los objetos, puede dar comportamientos no esperados. Por eso muchos programadores prefieren usar un bucle for/of
con Object.keys()
en vez de un for/in
.
Si el cuerpo de un for/in
elimina una propiedad que no ha sido enumerada todavía, esta no será enumerada. Si define una nueva propiedad en el objeto, tampoco será enumerada.
Otra categoría de los estamentos de JS son los saltos. Como su nombre implica, estos causan que el interprete de JS salte a una nueva localización en el código. El estamento break
hace que el intérprete salte al final del loop o de otro estamento. continue
hace que el intérprete pase del resto del cuerpo de un loop y vuelva al principio del mismo a empezar una nueva iteración. A su vez JS, permite etiquetar estamentos, de tal manera que break
y continue
puedan identificar el loop objetivo a saltar u otro estamento.
El estamento return
hace que el intérprete salte de la invocación de una función de vuelta al código que la invocó y a su vez remite el valor de la invocación a dicha función. El estamento throw
es una especie de generador dentro de una función, lanzando una exepción y es diseñado para trabajar con estamentos try/catch/finally
, los cuales nos permiten establecer bloques de código donde manejar errores. Es un estamento de salto complicado, pues cuando una excepción es lanzada el intérprete saltará al manejador más cercano, que puede estar dentro de la misma función o en el stack que rodea a la invocación dela misma.
TODO: IMPLEMENTAR
El estamento break
, usado en solitario, hace que el loop o switch
más cercano que lo envuelve termine inmediatamente. Su sintáxis es simple:
break;
Como causa que un loop o switch
salgan, esta forma del estamento break
solo es legal si aparece dentro de dichos estamentos. En los loops es usado normalmente para causar que salgan de forma prematura, da igual la razón, si no hay necesidad de completar el loop. Cuando un loop tiene condiciones de finalización complejas, es normalmente más facil implementar dichas condiciones con estamentos break, en vez de intentar expresar todo en una única expresión.
Por ejemplo, el siguiente código busca los elementos de un array que tienen un valor particular. El loop termina de manera normal cuando alcanza el final del array, pero también con un break
si alcanza su objetivo de búsqueda:
for (let i = 0; i < a.length; i++) {
if (a[i] === target) {
break;
}
}
TODO: Implementar información con etiqueta
El estamento continue
es similar al estamento break
. En vez de salir de un loop, continue
reinicia el loop hasta la siguiente iteración. Su sintaxis es simple:
continue;
Solo puede ser usado dentro de un loop, en cualquier otro lado causará un error de sintáxis.
Cuando el estamento continue
se ejecuta, la iteración del loop actual finaliza, y la siguiente iteración empieza. Dependiendo del tipo de loop:
TODO: Completar
- En un loop
while
, la expresión al inicio del loop es testeada de nuevo y el loop empieza desde el top del mismo si esta expresión se evalua comotrue
. - En un loop de tipo for, la expresión de incremento es evaluada y la expresión de test vuelta a ejecutar para determinar si la iteración debe ser realizada.
La diferencia en comportamiento de continue
entre un bucle while
y un for
es: que un loop while
retorna directamente a la condición, pero el for
evalua el incremento y retorna a la condición.
Hemos considerado el comportamiento de un loop for
en terminos de equivalencia de un while
, Como el estamento continue
se comporta diferente entre estos dos loops, no es posible simular un for
perfectamente como un while
en solitario..
El siguiente ejemplo muestra un estamento continue
usado para saltar el resto de la iteración si el error ocurre:
for (let i = 0; i < data.length; i++) {
if (!data[i]) {
continue; // No puedo continuar si no tengo datos
}
total += data[i];
}
Teniendo en cuenta que la invocación a funciones son expresiones y todas las expresiones evaluan a valores. Un estamento return
dentro de una función especifica el valor de la invocación de dicha función. Su sintáxis es simple:
return expression;
Un estamento return
puede aparecer solo dentrod del cuerpo de una funcuón. Si aparece en cualquier otro lado JS lanzará un un error de sintáxis. Cuando un estamento return
es ejecutado, la función que lo contiene retornará un el valor de la expresión al que la invoca. Por ejemplo:
function square(x) {
return x * x; // Retorna la evaluación de la expresión de su derecha
}
square(2); // => 4
Cuando uno existe dicho estamento return
, la función al ser invocada simplemente ejecuta cada estamento de su cuerpo de uno en uno hasta que alcanza el final de la misma y retorna el control a quien la invoca. En este caso, la expresión de invocación siempre evaluará a undefined
. El estamento return
normalmente aparece como el último estamento de una función, pero no es necesario: la función retorna el control a su invocador cuando return
es ejecutado, independientemente de si quedan estamentos por ejecutar en su cuerpo.
El estamento return
puede ser usado sin expresión para hacer que una función retorne undefined
a su invocador, por ejemplo:
function displayObject(o) {
if (o == null) {
return; // Retorna inmediatamente si el objeto null o undefined
}
// ....
}
No se pueden meter saltos de línea entre el return y su expresión.
Una excepción es una señal que indica alguna clase de condicion excepcional o error que ocurre en el código. Lanzar una excepción es como señalizar un error o dicha condición excepcional. Capturar la excepción y manejarla significa tomar las acciones necesarias para recuperarse de dicha excepción. En JS, las ecepciones son lanzadas donde quiera que un error en tiempo de ejecución ocurra o donde un estamento throw
esté implementado. Las excepciones son capturadas con el estamento try/catch/finally
.
La sintáxis de un throw
es:
throw expresión;
La expresión puede evaluarse a un valor de cualquier tipo. Puedes lanzar un número que representa un código de error o un string que contiene un mensaje legible por otros programadores. La clase Error
y sus subclases son usadas cuando el intérprete JS lanza un error por si mismo y también podemos usarlas nosotros. Un objeto Error
tiene una propiedad llamada name
que especifica el tipo de error y una propiedad message
que guarda el string pasado al constructor de la función.
function factorial(x) {
if (x < 0) {
throw new Error("x must not be negative"); // se lanza una excepción ante un argumento inválido
}
// de otra manera se continua computando el valor de retorno
let f;
for (f = 1; x > 1; f *= x, x--);
return f;
}
Cuando una excepción es lanzada, el intérprete de JS finaliza inmediatamente la ejecución del programa y salta a manejador de excepción más cercano. Los manejadores de excepciones se escriben usando las cláusulas catch
del estamento try/catch/finally
. Si el bloque de código que lanza la excepción no esta asociada a una cláusula catch
, el intérprete busca el código que la envuelve para comprobar si existe un manejadore de excepción.
En otras palabras, si la excepción se lanza en una función que no contiene un estamento try/catch/finally
que la maneje esta es propagada a lo largo del bloque que la invoca. Si al propagarse en la cadena de invocaciones, no se encuentra ningún manejador, la excepción es tratada como un error y reportada al usuario.
El estamento try/catch/finally
es un mecanismo de manejo de excepciones en JS. La cláusula try
de este estamento, simplemente define el bloque del código cuya excecpión ha de ser manejada. El try
es seguido de una cláusula catch
, la cual es un bloque que es invocado cuando una excepción ocurre en cualquier lado del bloque try
. La clausula catch
es seguida de un bloque finally
cuyo cuerpo contiene el código de limpieza que es garantizado de ser ejecutado independientemente de lo que ocurra en el try
. Ambos, catch
y finally
son opcionales, pero un try
debe ser acompañado como mínimo de uno de los dos. Como los tres son cláusulas que definen bloques de código, deben ser acompañados de llaves y no son omisibles, incluso si la cláusula solo contiene un estamento:
try {
/*
Normalmente este código es ejecutado sin problemas, pero hay veces que una excepción es lanzada,
bien de manera directa o bien porque parte del código del bloqe use alguna función que la lanza.
*/
} catch (e) {
/*
Este estamento es ejecutado si y solo si el bloque try lanza una excepción.
Pudiendo usar la variable local "e" que se refiere al objeto error o valor lanzado por el throw.
Aqui podemos manejar la excepción, ignorarla o lanzar una nueva si es necesario.
*/
} finally {
/*
Este bloque de código siempre sera ejecutado, independientemente de si existe o no una excepción el el try:
Será ejecutado si:
1. Normalmente, cuando se alcanza el final del bloque try
2. Porque se lanze un break, continue, o return
3. Porque una excepción es manejada por el catch
4. Por una excepción no manejada que todavia se propaga
*/
}
Nota que el catch
es acompañado de un identificador entre paréntesis. Este identificados es como el parámetro de una función. Cuando una excepción es capturada, el valor asociado a la excepción (un objeto Error
, por ejemplo) es asignado a este parámetro. El identificador asociado con la cláusula catch
tiene un scope de bloque, es decir, solo está definido dentro del catch
.
Si usamos el ejemplo anterior, la función de factoriales con un try/catch/finally
:
try {
const n = Number(prompt("Inserta un entero positivo"));
const f = factorial(n); // usamos la funcion factorial asumiendo que el valor de n es válido
alert(`${n}! = ${f}`);
} catch (e) {
alert(e); // si el input que inicializa a n en el try es inválido y la excepción es lanzada la capturamos y mostramos por pantalla
}
El anterior ejemplo no usa la cláusula finally
. finally
no es tan usada como catch
, pero puede ser útil. La cláusula finally
es ejecutada independientemente de lo que ocurra en el try
. Es usada generalmente para limpiar después del try
.
En un caso normal, el intérprete de JS alcanzará el final del bloque try
y procedera con el bloque finally
, que realiza la limpieza necesaria. Si el intérprete abandona el try
por causa de un estamento return
, continue
o break
. finally
es ejecutado antes de que el intérprete salte a su nuevo destino.
Si una excepción ocurre en un bloque try
y tiene un bloque catch
asociado para manejar dicha excepción, el intérprete primero ejecutará el bloque catch
y finalmente el bloque finally
. Si no hay un bloque catch
para manejar la excepción, el intérprete primero ejecutará el finally
y luego saltará al catch
más cercano en el stack de llamadas.
Si un bloque finally
causa un salto debido a un estamento return
, continue
o break
, o por llamar a un método que lanza una excepción, el intérprete abandonara cualquier salto que estuviera pendiente y ejecutará uno nuevo. Por ejemplo, si una cláusula finally
lanza una excepción, dicha excepción reemplaza a cualquiera que estuviera en proceso de ser lanzada. Si finally
lanza un return
, el método retorna de manera normal, incluso si hay una excepción que ha sido lanzada y no manejada de todo.
try
y finally
pueden ser usados juntos sin catch
. En este caso, el bloque finally
simplemente es código de limpieza cuya ejecución está garantizada.
TODO: IMPLEMENTAR
TODO: IMPLEMENTAR