0% encontró este documento útil (0 votos)
33 vistas

JavaScript Como Lenguaje Interpretado

Cargado por

rodolfo
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
33 vistas

JavaScript Como Lenguaje Interpretado

Cargado por

rodolfo
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 209

JavaScript como lenguaje interpretado

JavaScript es un típico lenguaje interpretado. Si ejecutamos un código


escrito en JavaScript en un navegador web, como está sucediendo en la
página que estamos leyendo actualmente (sí, sí, hay elementos escritos en
JavaScript en esta página también), el intérprete será el Motor JavaScript
integrado en el navegador. Esta no es la única forma de ejecutar código
JavaScript.

Tal vez hayas oído hablar de node.js. También es un intérprete, pero se


instala de manera independiente a los navegadores, en un entorno en el
sistema operativo de la computadora (puede ser macOS, Windows o
Linux). El uso de node.js te permite escribir programas en JavaScript que,
por ejemplo, convertirán tu computadora en un servidor.

Al comienzo de este párrafo, simplificamos un poco las cosas. JavaScript


es un lenguaje interpretado, de eso no hay duda. Y, de hecho, ejecutar un
programa escrito en JavaScript parece como si estuviéramos ejecutando
nuestro código fuente (es decir, el código que escribimos) paso a paso. Sin
embargo, puedes llegar a encontrar información sobre este lenguaje, y
sobre intérpretes, que son un poco diferentes.

La mayoría de los motores de JavaScript modernos utilizan la


técnica Compilación Just In Time - Justo a Tiempo (Compilación JIT). Esta
técnica consiste en compilar fragmentos de código durante la ejecución del
programa (más de una sola instrucción) y permite aumentar su
rendimiento. Sin embargo, desde el punto de vista del usuario, dicho
cambio es prácticamente imperceptible: sigue pareciendo que solo el
intérprete está ejecutando el código fuente, instrucción por instrucción.

Independientemente del lenguaje que elijas, algunas cosas permanecen


igual mientras escribes el programa. En primer lugar, una etapa
importante, y probablemente la más difícil, de este proceso es definir
correctamente el problema que queremos resolver. Solo entonces tratamos
de encontrar la solución óptima, que finalmente presentaremos en forma
de un programa escrito en el lenguaje elegido.

Entonces, antes de comenzar a explicarle algo a la computadora, en otras


palabras, escribir un programa, debes comprender exactamente qué
deseas lograr y cómo deseas lograrlo. En segundo lugar, la solución que
proponemos y escribimos en forma de programa debe ser 100% precisa: la
computadora no puede adivinar nada.
Un ejemplo simple de un campo ligeramente diferente: en algún momento
de tu vida, probablemente compraste un mueble que requería ensamblaje.
Montarlo es un problema con el que el comprador, ha tenido que cargar.
Para que pueda hacer frente a esta tarea, obtiene un conjunto de
instrucciones que lo guiarán a través de todo el proceso. Está actuando
como intérprete en este punto, utilizando un programa que le permitirá
completar la tarea. El éxito de tu misión depende de la calidad de estas
instrucciones, de que sean precisas, y de que no provengan de otro
mueble. Al final, puede resultar que no hayas construido los muebles de
tus sueños, sino una construcción surrealista de otra dimensión.

Para que las instrucciones sean buenas, quien las desarrolle debe saber
exactamente qué deben ilustrar, en qué orden deben realizar ciertas
acciones, en qué etapas es más fácil confundirse, etc. Por supuesto, deben
saber qué efecto se va a lograr al final.

Algunas palabras más sobre JavaScript

Como mencionamos antes, JavaScript es un lenguaje de programación


interpretado. Como la mayoría de los lenguajes interpretados, también es
un lenguaje de alto nivel (es decir, relativamente fácil de entender para las
personas y que nos separa de los detalles del hardware).

A principios de los 90, todas las páginas web eran estáticas. Las cosas
cambiaron en 1995 cuando la corporación Netscape contrató a Brendan
Eich y le encargó que desarrollara un nuevo lenguaje para su producto, el
navegador web Netscape Navigator. El nuevo lenguaje se llamó LiveScript,
pero poco después se cambió su nombre a JavaScript. Su tarea principal
era agregar dinámicas a los sitios web, lo que permitiría, por ejemplo, una
interacción más compleja con el usuario. Y así comenzó la carrera de
JavaScript.

Programación del lado del cliente y del lado del servidor

El uso de JavaScript en los sitios web, que con el tiempo se ha vuelto cada
vez más complejo y, a menudo, contiene una lógica muy sofisticada, se
denomina programación del lado del cliente. El código a ejecutar se carga
junto con la página en el navegador, del lado del usuario, y el intérprete
que forma parte del navegador web permite su ejecución.

Hoy en día, JavaScript es el único lenguaje compatible con todos los


principales navegadores web, y aproximadamente el 95 % de las páginas
web de todo el mundo incorporan código JavaScript en ellas. Desde el
principio, las páginas web utilizaron JavaScript en el lado del cliente para
agregar interactividad y cambiar dinámicamente el contenido.
Ahora es mucho más que eso, ya que JavaScript ofrece muchos
frameworks sobre los cuales podemos construir aplicaciones web y redes
sociales enormes y complejas (probablemente hayas escuchado los
nombres de frameworks como React o Angular).

Todo esto puede funcionar en una variedad de equipos, desde estaciones


de trabajo de alto rendimiento hasta simples teléfonos inteligentes. Gracias
al poder de JavaScript, podemos pedir comida, jugar en el navegador, ver
películas en plataformas de transmisión y estar en contacto constante con
las personas importantes para nosotros. JavaScript es tan popular que
continuamente se dedica más y más esfuerzo a usarlo, no solo como una
solución del lado del cliente.

Con el tiempo, JavaScript comenzó a aparecer en otras áreas, como en la


programación de lado del servidor de aplicaciones web complejas, también
llamadas back-end. Estos programas se ejecutan en servidores,
procesando datos (por ejemplo, de bases de datos), que después del
procesamiento estarán disponibles en el lado del cliente. La flexibilidad de
este lenguaje y su relativa sencillez lo han hecho mucho más aplicable, por
ejemplo, en aplicaciones móviles, o incluso en la programación de UAVs -
Vehículo Volador No Tripulado (algunos drones ejecutan programas
escritos en este lenguaje).

¿Es este el lenguaje de programación perfecto? - Desventajas

Decimos que JavaScript es un lenguaje maduro, lo que significa que la


mayoría de las funciones ya están implementadas y son estables, y
probablemente no veremos grandes cambios en el lenguaje. Desde 2015,
muchos aspectos de JavaScript han cambiado y se han agregado muchas
características nuevas. Muchos de estos cambios se introdujeron para
facilitar la migración a JavaScript para los programadores que conocen
otros lenguajes populares, de los cuales JavaScript originalmente difería
bastante en ciertos aspectos, como el manejo de objetos. Todavía podemos
usar el lenguaje de la manera antigua, pero se recomienda usar el
JavaScript moderno.

No existen soluciones ideales, por lo que no existen buenos lenguajes de


programación para todas las aplicaciones. Cada uno de ellos tiene sus
propias limitaciones, y JavaScript no es diferente. A pesar de su
popularidad y éxito, JavaScript no es un lenguaje de programación
perfecto. Debido a su naturaleza, no es adecuado para ciertas
aplicaciones. Por ejemplo, no tiene sentido usarlo para escribir programas
que requieran cálculos matemáticos avanzados o un rendimiento muy alto.
Algunas limitaciones se deben al propio concepto del lenguaje, pero la gran
mayoría están relacionadas con la plataforma en la que lo usamos. Esto es
especialmente visible cuando se escribe código para ser ejecutado en un
navegador, que como dijimos anteriormente se denomina del lado del
cliente. En tal situación, la funcionalidad de JavaScript está limitada por
el hecho de que los navegadores, por razones de seguridad, ejecutan
código de secuencia de comandos en un entorno sandbox (un entorno
separado del mundo exterior), que no permite acceso a archivos y recursos
locales (es decir, aquellos archivos que están en la computadora donde se
inicia el navegador).

Otro inconveniente es que como el código no está compilado, entra en el


navegador de la misma forma, o muy similar, a la que lo escribimos
nosotros. ¿Por qué es esto una desventaja? Esto se debe a que todos
pueden ver nuestra solución en una forma fácil de leer y usarla (ya sea en
fragmentos o incluso en su totalidad) sin nuestro permiso.

Algo de ayuda aquí puede ser la ofuscación del código, que consiste en
transformar nuestro script listo en una forma un poco menos legible (por
ejemplo, generando nombres aleatorios cortos de variables y funciones,
eliminando los signos de final de línea, etc.), pero el simple hecho es que si
alguien quiere robar nuestro código JavaScript, es muy poco lo que
podemos hacer para detenerlo.

Prev¿Es este el lenguaje de programación perfecto? - Ventajas

Por otro lado, JavaScript tiene muchas ventajas sobre otros lenguajes de
programación, y una de las más grandes es una comunidad muy activa y
solidaria. Es fácil encontrar soluciones a problemas comunes y encontrar
ayuda en general. Esto también significa que se desarrollan activamente
herramientas que funcionan con JavaScript.

Otra gran ventaja es una gran cantidad de frameworks y librerías listas


para usarse que brindan la mayoría de las funcionalidades y
características comúnmente requeridas. El lenguaje en sí es relativamente
fácil de aprender y nos permite centrarnos en el trabajo en lugar de luchar
con la sintaxis (es decir, la forma de construir las instrucciones que
componen el código de nuestro programa).

Además, JavaScript no requiere que compres herramientas costosas para


trabajar con él, y las herramientas realmente buenas ya están integradas
dentro de tu navegador web. Por último, pero no menos importante, los
grandes jugadores como Google, Facebook y Mozilla apoyan activamente
las herramientas de JavaScript y su desarrollo.

Sin embargo, lo que es una ventaja para unos puede resultar una
desventaja para otros. Un ejemplo de esto es el manejo dinámico de los
tipos de datos en JavaScript. Consiste en que podemos almacenar datos de
cualquier tipo en una variable (una variable es un contenedor en el que
almacenamos los datos que utilizaremos).

Por ejemplo, durante la ejecución del programa, podemos almacenar el


número 10 en una variable, y en el siguiente paso usar la misma variable
para almacenar la cadena "abc" (borrando el valor anterior
automáticamente. No te preocupes si no entiendes en este momento,
porque cubriremos todos estos términos más adelante).

Por lo general, esto es muy conveniente, pero varias personas han


encontrado que esta característica del lenguaje es una desventaja. En su
opinión, facilita que un programador cometa errores en ciertas situaciones.
Al agregar manejo estático de los tipos de datos, donde una variable solo
puede contener un tipo de dato (por ejemplo, números) durante la
ejecución del programa, se creo un nuevo lenguaje llamado TypeScript.

Recuerda también que si aprendes a programar en un lenguaje,


normalmente te será mucho más fácil aprender el siguiente, que por
alguna razón puede ser mejor para resolver un problema en particular.

Pero comencemos ahora con JavaScript, que, debido a su sintaxis flexible


y simple, es perfecto para aprender como primer lenguaje.

Preparémonos para trabajar

Como mencionamos anteriormente, JavaScript se puede usar en varios


entornos, aunque la mayoría de las veces será un navegador web o un
servidor con un entorno node.js. Cada entorno impone una manera un
poco diferente de usar este lenguaje, y aparecen algunos mecanismos o
funciones propias del mismo. Sin embargo, la parte esencial del lenguaje,
su núcleo, sigue siendo el mismo. En esta parte del curso, aprenderemos a
programar utilizando esta parte central e invariable de JavaScript: cómo
declarar variables, escribir funciones, instrucciones condicionales o
bucles; todo esto será igualmente utilizable en cualquier entorno en el que
decidamos utilizar este lenguaje.

Programar en cualquier lenguaje no es algo fácil de aprender, y es posible


que te sientas abrumado por tanta información nueva. Si eres persistente
y te concentras, estarás escribiendo scripts simples en poco tiempo, y no
hay otra forma de aprender a programar que escribir muchísimo código.

Lo más importante es que no te rindas incluso cuando estés atascado:


tómate un descanso, sal a caminar, vuelve a hacerlo con una mente fresca
e inténtalo de nuevo. Al final, el ser constante te hace ganar la carrera.

Herramientas de desarrollo

Como cualquier otra tarea, la programación requiere las herramientas y el


espacio de trabajo adecuados. El desarrollo de software, en la mayoría de
los casos, requiere un editor de código y un compilador o intérprete de
un lenguaje determinado. Este es el conjunto mínimo, que podemos
ampliar según sea necesario con otras herramientas.

En esta etapa del curso, además del editor e intérprete de código


JavaScript, también podemos utilizar el depurador, que es una
herramienta que nos permite, entre otras cosas, pausar el programa en el
lugar indicado y analizar su estado actual (por ejemplo, los valores de las
variables indicadas).

Por supuesto, las herramientas en cuestión deberán ejecutarse en la


computadora. En esta etapa, su rendimiento no es particularmente
importante, y cualquier equipo que pueda manejar las tareas normales de
oficina será suficiente, por lo que es muy recomendable trabajar desde una
computadora de escritorio o portátil.

No se puede negar que el tamaño del monitor afectará la comodidad de tu


trabajo. Cuanto más grande sea el monitor, más fácil será colocar el editor
de código, el intérprete y otro contenido (por ejemplo, este curso) uno al
lado del otro. En circunstancias normales de trabajo, los programadores
suelen utilizar varios monitores.

No importa el sistema operativo, ya que se puede encontrar la herramienta


adecuada para Windows, macOS y Linux.

Entorno de desarrollo en línea

Los entornos en línea, son sitios que actúan como un editor sencillo y un
entorno de ejecución. Todos ellos tienen conjuntos similares de
características. Tienen diferentes interfaces de usuario, pero en principio
se comportan de manera similar. Te permiten escribir código, ejecutarlo
con fines de prueba y, en la mayoría de los casos, compartirlo con otros
usuarios.

En el caso de JavaScript, donde la preparación de un entorno local que


funcione se reduce a instalar un editor de código y ejecutar el navegador,
no son tan importantes como los entornos de desarrollo regulares. Se
utilizan principalmente como plataformas de prueba y capacitación, o
lugares para publicar soluciones de muestra a problemas de
programación.

Entre los programadores de JavaScript, los más populares son los


siguientes:

 JSFiddle
 CodePen
 JsBin
 Plunker

Durante el curso, utilizaremos un entorno en línea integrado con la


plataforma de capacitación. OpenEDG proporciona un entorno simple para
escribir y ejecutar código en varios lenguajes de programación, incluido
JavaScript. Gracias a eso, podrás practicar todo lo que hablamos de
inmediato.

Sin embargo, no olvides que esta plataforma es una solución puramente


didáctica y de prueba, y ciertamente no puede usarse como un entorno de
desarrollo completo. Sin embargo, es ideal para nuestras necesidades, ya
que en la mayoría de los casos podremos olvidarnos del entorno web de los
programas escritos en JavaScript, incluidos los elementos HTML. Esto nos
permitirá centrarnos únicamente en aprender el lenguaje JavaScript en sí.

Sin embargo, se recomienda encarecidamente que también configures tu


propio entorno de desarrollo local. No es difícil, como descubrirás de
inmediato, y te permitirá realizar algunos ejercicios de una forma mucho
más parecida a cómo lo harías durante el desarrollo normal de software.
Si, durante el curso, alguno de los ejercicios debe realizarse en un entorno
de este tipo, lo indicaremos claramente.

Entorno de desarrollo local

Como escribimos anteriormente, los requisitos de JavaScript para el


entorno de desarrollo son muy modestos. En la mayoría de los casos,
especialmente al comienzo del desarrollo, solo tres elementos son
suficientes: un editor de código, un intérprete (es decir, un entorno de
arranque) y un depurador.

Dependiendo del nivel de sofisticación, la complejidad del proyecto escrito


o el entorno para el que escribimos nuestros programas (del lado del
cliente, o del lado del servidor), es posible que también se necesiten otras
herramientas.
Existirán, entre otras:

 Administradores de paquetes: que permiten la gestión de librerías


(que contienen soluciones listas para usar, que podemos emplear en
nuestros programas) o componentes del entorno de desarrollo (por
ejemplo: npm o yarn).

 Ejecutores de tareas y empaquetadores de módulos: utilizados,


en términos simples, para automatizar el proceso de desarrollo de
software y unir el código resultante de muchos archivos y librerías
(por ejemplo Grunt o Webpack).

 Frameworks de pruebas: permiten realizar pruebas automáticas


para la corrección de nuestro programa, al buscar posibles errores
(por ejemplo Mocha, Jasmine, o Jest).

 Analizadores de seguridad: como puede adivinar, se usan para


controlar la seguridad de nuestra solución (por ejemplo, Snyk,
RetireJS u OWASP Dependency Check).

Editor de Código

El código de casi todos los lenguajes de programación se compone de


alguna forma de texto. Entonces, para escribir el código, necesitamos un
editor de texto. Pero debe ser una aplicación que escriba texto sin formato
(no puede ser un editor de texto enriquecido, como MS Word). En otras
palabras, solo un bloc de notas simple que pueda escribir archivos .txt es
suficiente para escribir código, aunque es mucho más fácil si se usa un
editor de código dedicado. El mercado está repleto de editores de código
profesionales, tanto gratuitos como de paga. Algunos de ellos son
universales, mientras que otros son exclusivos de lenguajes específicos. La
principal ventaja de usar un editor de código dedicado es el resaltado de
sintaxis, el autocompletado de texto y la verificación de errores. Esto
mejora la eficiencia del trabajo y la comprensión del código, y reduce la
cantidad de errores y errores tipográficos. Hay muchos buenos editores de
código, pero puede ser realmente difícil seleccionar uno que funcione bien
para ti.

Aquí se mencionan algunos populares:

 Visual Studio Code

[Windows, macOS, Linux]


Potente editor de código gratuito para uso personal y comercial. Se
ha convertido rápidamente en uno de los favoritos cuando se trata
de desarrollo web. Tiene funciones integradas como un depurador de
JavaScript y herramientas para optimizar los proyectos web.
También es altamente personalizable a través del sistema de
extensiones (existen muchas adiciones especialmente para el
lenguaje JavaScript).

 WebStorm

[Windows, macOS, Linux]

Un entorno de desarrollo comercial popular, en el que el editor de


código es solo uno de los elementos más pequeños en un gran
conjunto de herramientas que mejoran el desarrollo de código (por
ejemplo, soporta realizar pruebas al código). Destinado para
proyectos grandes, puede resultar demasiado pesado y complejo
para programas pequeños. Aunque está destinado a un uso
comercial, es posible obtener una licencia educativa gratuita.

Intérprete

Ya hemos hablado un poco sobre el intérprete y su función. Funciona


como un entorno de ejecución para nuestro programa. Comprueba si
hemos cometido algún error formal, por ejemplo, cometer un error
tipográfico en el nombre de una función u olvidar cerrar un paréntesis, y
luego ejecuta el programa instrucción por instrucción.

La elección del intérprete de JavaScript dependerá de la plataforma para la


que escribamos nuestro software. Por ejemplo, si queremos escribir una
aplicación sencilla del lado del servidor, es casi seguro que elegiremos el
entorno node.js, que tendremos que instalar directamente en nuestro
sistema operativo. En el caso del software del lado del cliente, nuestro
intérprete será simplemente el navegador web que ya tienes instalado
(porque, ¿de qué otra forma estarías leyendo este curso?).

Nuestro curso trata sobre los fundamentos de JavaScript, es decir,


aquellos elementos del lenguaje que serán igualmente útiles en soluciones
móviles, del lado del cliente y del lado del servidor. Así podemos
practicarlos en cualquier entorno, utilizando cualquier intérprete. La forma
más fácil de hacer esto es limitarse a un navegador web.

Como hemos dicho anteriormente, prácticamente todos los navegadores


tienen motores (o intérpretes) JavaScript integrados, pero recomendamos
encarecidamente usar Chrome de Google, o FireFox de Mozilla . Ambos
son conocidos por su eficiencia y herramientas avanzadas integradas para
desarrolladores web (ese eres tú). Están disponibles para Windows, macOS
y Linux.

Intérprete

Ya hemos hablado un poco sobre el intérprete y su función. Funciona


como un entorno de ejecución para nuestro programa. Comprueba si
hemos cometido algún error formal, por ejemplo, cometer un error
tipográfico en el nombre de una función u olvidar cerrar un paréntesis, y
luego ejecuta el programa instrucción por instrucción.

La elección del intérprete de JavaScript dependerá de la plataforma para la


que escribamos nuestro software. Por ejemplo, si queremos escribir una
aplicación sencilla del lado del servidor, es casi seguro que elegiremos el
entorno node.js, que tendremos que instalar directamente en nuestro
sistema operativo. En el caso del software del lado del cliente, nuestro
intérprete será simplemente el navegador web que ya tienes instalado
(porque, ¿de qué otra forma estarías leyendo este curso?).

Nuestro curso trata sobre los fundamentos de JavaScript, es decir,


aquellos elementos del lenguaje que serán igualmente útiles en soluciones
móviles, del lado del cliente y del lado del servidor. Así podemos
practicarlos en cualquier entorno, utilizando cualquier intérprete. La forma
más fácil de hacer esto es limitarse a un navegador web.

Como hemos dicho anteriormente, prácticamente todos los navegadores


tienen motores (o intérpretes) JavaScript integrados, pero recomendamos
encarecidamente usar Chrome de Google, o FireFox de Mozilla . Ambos
son conocidos por su eficiencia y herramientas avanzadas integradas para
desarrolladores web (ese eres tú). Están disponibles para Windows, macOS
y Linux.

Depurador
Los programas de computadora son bestias complicadas, miles o incluso
millones de líneas de código (pero tranquilo, comenzaremos con solo unas
pocas). Con tal complejidad y tamaño, es imposible producir código sin
errores. Algunos tipos de errores, especialmente los lógicos (formalmente,
el programa está escrito correctamente, pero probablemente inventamos la
solución incorrecta al problema), solo se pueden encontrar mientras el
programa se está ejecutando y, a menudo, solo en circunstancias
especiales. Es realmente difícil averiguar qué sucede exactamente dentro
de un programa que se ejecuta a la velocidad del rayo, y para esos
problemas existen los depuradores.

Un depurador es una herramienta que te permite alentar o incluso


detener la ejecución de un programa, ejecutar instrucciones paso a paso,
ver y analizar el estado del programa en un momento dado.

Afortunadamente, en el momento en que decidimos usar el navegador web


como nuestro entorno de arranque e intérprete de JavaScript, también
obtuvimos un depurador. Todos los navegadores modernos están
equipados con las herramientas de desarrollo. Durante el funcionamiento
normal, son invisibles y tenemos que habilitarlos en las opciones del
navegador (más sobre esto en el próximo capítulo).

Dependiendo del navegador, allí encontraremos varias herramientas, pero


seguramente habrá:

 El inspector, que nos permitirá, por ejemplo, analizar los elementos


HTML individuales de un sitio web abierto.

 La consola de JavaScript, que en primer lugar muestra toda la


información sobre los errores y, en segundo lugar, nos permite
ejecutar comandos de JavaScript únicos en el contexto de la página
actual.

 El depurador, que entre otras cosas, muestra los valores actuales de


las variables y te permite pausar la ejecución del código en el lugar
indicado y ejecutarlo paso a paso (es decir, ejecutar instrucciones
del programa).

¿Cómo habilitas las herramientas para desarrolladores?


Desafortunadamente, no hay una respuesta única; depende del navegador
que estés utilizando (a veces también de su versión) y del sistema
operativo. Las interfaces del navegador cambian con bastante frecuencia,
por lo que es mejor aprender los atajos correctos en lugar de buscar la
opción correcta en el menú. Prueba las siguientes combinaciones de
teclas:

 Sistemas operativos Windows y Linux, todos los navegadores


comunes excepto Internet Explorer y Edge:

 Sistema operativo Windows, Internet Explorer y Edge:

 Sistema operativo macOS, todos los navegadores comunes:

En el próximo capítulo, volveremos a este tema y aprenderemos algunas


cosas más sobre estas útiles herramientas.

En el próximo capítulo escribiremos nuestra primera pieza de código


JavaScript. Lo probaremos en primer lugar en el entorno de ejecución
integrado con nuestra plataforma de formación. También lo utilizaremos
para comprobar cómo funciona nuestro entorno de desarrollo local. Por lo
tanto, hay que asegúrarse de que las herramientas seleccionadas estén
instaladas y de que puedas iniciarlas. Si aún no sabes qué elegir, te
sugerimos usar el entorno local con Visual Studio Code y
el Chrome (navegador web con intérprete de JavaScript y depurador).

El Programa "¡Hola, Mundo!"

¿Por qué "¡Hola, Mundo!"? Durante casi 50 años, esta frase y sus
derivados han marcado a alguien aprendiendo un nuevo lenguaje de
programación, aunque es más una tradición que otra cosa. La frase se usó
hace mucho tiempo en un libro muy importante sobre el lenguaje C, pero
el texto en sí no es relevante ahora.

La idea es escribir algo en la pantalla utilizando un lenguaje específico.


Primero, nos permite ver la sintaxis básica del lenguaje y compararlo con
otros lenguajes de programación. En segundo lugar, es un programa muy
simple y cualquiera puede escribirlo o copiarlo fácilmente de Internet y
verificar si sus herramientas y su entorno están configurados
correctamente. En tercer lugar, es un programa que genera algo, por lo
que proporciona información sobre si se ejecutó correctamente o no.
En el caso de JavaScript del lado del cliente, mostrar algo en la pantalla se
puede entender de dos maneras.

Primero, el JavaScript del lado del cliente siempre se ejecuta en el contexto


de un sitio web y te permite manipular elementos de ese sitio web.
Entonces podemos, por ejemplo, usar la función apropiada para insertar
algún texto, cambiar un título, crear una tabla, etc. en la página. De esta
manera, controlamos la parte visual del sitio web.

Segundo, podemos usar la consola como una pantalla para escribir


alguna información. La consola, como mencionamos en el capítulo
anterior, es parte de las herramientas del desarrollador. Por lo tanto, no
está visible de forma predeterminada y debe estar habilitada
correctamente (también escribimos sobre esto en el capítulo anterior). Para
nuestras necesidades, será mucho más cómodo utilizar la consola, ya que
evitaremos la necesidad de un análisis exhaustivo de la estructura del sitio
web.

Pero, ¿qué es realmente una consola? En primer lugar, es un lugar donde


se muestran varios mensajes, normalmente invisibles para el usuario del
navegador. Estos mensajes pueden, por ejemplo, ser generados por el
intérprete de JavaScript tras encontrar un error o si lo imprimimos,
llamando a la función adecuada. En segundo lugar, podemos ejecutar
comandos de JavaScript individuales en la consola, que se ejecutarán en
el contexto de la página web actualmente cargada (se hablará un poco más
sobre eso en un momento).

La función básica que nos permite escribir información en la consola es


llamada console.log. Entonces, para referirnos al eterno "¡Hola, Mundo!",
deberíamos mandarlo llamar de la siguiente manera:

console.log("¡Hola, Mundo!");

Podemos tratar a console.log como una función*. De hecho, la función es


solo un registro y la consola es el objeto al que pertenece la función.

*Este tipo de función, perteneciente a un objeto, generalmente se


denomina método. Pero una vez más, por el momento, para simplificar
ciertas cosas, supongamos que se trata de una función ordinaria: no nos
molestará en absoluto (aprenderemos sobre los objetos mucho más
adelante).

Una función es un fragmento de código que te permite realizar una tarea


específica (en nuestro caso, mostrar algo en la consola). Las funciones a
menudo toman argumentos, en otras palabras, datos que usarán durante
la operación. En JavaScript, ejecutamos una función llamándola, y la
llamamos escribiendo su nombre seguido de un par de paréntesis, donde
se proporcionan los argumentos (si la función no necesita argumentos, los
paréntesis se dejan vacíos). En nuestro ejemplo, el argumento es el texto
que queremos mostrar. Toma en cuenta que para indicar que "¡Hola,
Mundo!" es el texto, lo ponemos entre comillas.

Para que el intérprete sepa dónde termina el comando, colocamos un


punto y coma al final de la llamada a la función. En este caso, el intérprete
se las arreglaría sin esa ayuda, pero es una buena costumbre terminar
cada comando con un punto y coma, para que no lo olvides cuando
realmente lo necesites.

Ya sabemos qué escribir, y la única pregunta ahora es, ¿dónde hacerlo?

Entorno de desarrollo en línea

Afortunadamente, nuestra plataforma utiliza un entorno en línea listo para


usarse, como mencionamos en el capítulo anterior. El entorno OpenEDG
te permite editar y ejecutar programas escritos en JavaScript. Toma en
cuenta que la parte de la pantalla dedicada a este entorno se divide en tres
partes. La parte superior es el editor, donde podemos elegir si editar un
archivo JavaScript, HTML o CSS (diremos algunas palabras sobre HTML y
CSS en un momento). Todos estos archivos juntos forman el código a
ejecutar en nuestro entorno de entrenamiento. Nos interesará
principalmente la pestaña del archivo JavaScript: app.js. En la parte
inferior izquierda de la pantalla, hay una ventana que simula la consola,
en la que aparecerán los mensajes del intérprete y la información que
escribimos. La ventana del lado derecho está diseñada para mostrar la
página en cuyo contexto se ejecuta nuestro código JavaScript. Esta
ventana será la menos útil en esta parte del curso.

En el editor, deberías ver la pieza de código que acabamos de discutir, que


contiene la función console.log. Intenta ejecutarlo. Debes presionar el
botón resaltado con el ícono de reproducción, ubicado directamente sobre
el editor. Como resultado, la ventana inferior que simula la consola debería
mostrar:

¡Hola, Mundo!

Ve al editor nuevamente y cambia la palabra "Mundo" por tu nombre.


Ejecuta el programa nuevamente y verifica lo que aparece en la ventana de
la consola. Felicitaciones, acabas de modificar un programa escrito en
JavaScript.

Podríamos discutir tu primer programa de JavaScript basado en este


ejemplo. Aprendiste la sintaxis, lo ejecutaste en línea, se comprobó su
efecto e incluso lo modificaste por ti mismo. Puedes probar todos los
ejemplos que discutimos en este curso de esta manera. Sin embargo, en el
capítulo anterior, te instamos a configurar tu entorno de desarrollo local.
Por lo tanto, sería bueno mostrarte cómo se puede ejecutar este ejemplo en
dicho entorno. Y esto requerirá una introducción un poco más larga.

Entorno de desarrollo local

El JavaScript del lado del cliente es un lenguaje de la web y solo existe en


el ecosistema web. En esta configuración, JavaScript no puede existir por
sí mismo. El código JavaScript debe estar incrustado en un documento
HTML. Cuando usamos el entorno en línea para ejecutar nuestro
programa, se nos ocultaron ciertos aspectos. Esta vez tendremos que
mirarlos más de cerca.

Unas palabras sobre HTML

HyperText Markup Language, o HTML, es un conjunto de etiquetas


utilizadas para describir la estructura de un sitio web. Nos permite dar a
una página el formato de un documento que contiene secciones,
encabezados, párrafos, listas y similares. HTML definitivamente está más
allá del alcance del curso actual, por lo que presentaremos solo
información básica al respecto, lo suficiente para que comprendas dónde y
cómo podemos ejecutar el código JavaScript asociado con una página
determinada.

Los tipos de etiquetas están predefinidos. Por ejemplo, la etiqueta que


especifica un párrafo es <p> y la etiqueta para el encabezado de primer
grado (el más grande) es <h1>. El nombre de la etiqueta debe colocarse
entre corchetes. Las etiquetas se suelen utilizar en pares, limitando un
área determinada del documento (tenemos una etiqueta de apertura y otra
de cierre). La etiqueta de cierre es diferente de la etiqueta de apertura
porque aparece una barra antes del nombre. Por ejemplo, un párrafo
puede verse así:

<p>un párrafo ordinario</p>

A menudo, las etiquetas pueden (y a veces deben) colocarse dentro del


rango de otras etiquetas. Por ejemplo, nuestro párrafo debe colocarse
dentro de la etiqueta <body>, que separa la parte principal de nuestro
documento.

<body>

<p>un párrafo ordinario</p>

</body>
Documento HTML

Intentemos crear un HTML sencillo que defina una página vacía.

<!DOCTYPE html>

<html>

<head>

<title>Empty Page</title>

</head>

<body>

</body>

</html>

Comencemos con la declaración <!DOCTYPE html>. Esta no es una


etiqueta típica, ya que se utiliza para informar al navegador que todo el
documento se ha preparado de acuerdo con HTML5. La descripción del
documento en si comienza con la etiqueta <html>, la cual junto con la
etiqueta </html> establece los límites del documento. Cualquier otra
etiqueta debe estar dentro de estas. Si una etiqueta dada tiene otro
contenido, habrá una etiqueta de cierre correspondiente, formando una
especie de contenedor.

La siguiente etiqueta, <head>, contiene información adicional sobre el


documento, que también debe colocarse en etiquetas. La más básica es la
etiqueta <title>, que establece el título de la página mayormente visible en
la barra de título del navegador. Después de <head> esta el elemento
<body>, y allí se debe colocar el contenido visible de la página web (por
ejemplo, nuestro párrafo).

La etiqueta <script>

El código JavaScript que ejecutará el navegador en la página debe


adjuntarse al HTML utilizando la etiqueta <script>, y existen dos formas
de hacerlo. El código se puede incrustar directamente dentro de las
etiquetas <script> y </script>, pero esto solo se recomienda cuando el
código es corto. Otro enfoque es utilizar el atributo "src" para apuntar a un
archivo separado que contiene el código JavaScript. Esto es especialmente
cierto cuando se va a usar el mismo código en varias páginas, porque
repetir exactamente el mismo código muchas veces es una mala práctica,
ya que cualquier cambio debe aplicarse a todos los archivos; y además,
aumenta artificialmente el tamaño de la página. La extensión del archivo
JavaScript es .js.

HTML es leído por el navegador línea por línea, y las etiquetas se ejecutan
justo en el momento en que el navegador analiza la
etiqueta <script> (analizar en lenguajes de programación significa un
análisis formal del código por parte de una máquina para comprender su
estructura). Generalmente las etiquetas <script> se insertan en el
encabezado de la página entre las etiquetas <head> y </head>, y podemos
insertar muchos de ellos en un archivo, por ejemplo, para incluir código
JavaScript de diferentes archivos. Este comportamiento se puede cambiar
para scripts externos señalados por el atributo "src" usando los
atributos "defer" o "async".

 defer: significa que el script debe ejecutarse después de cargar toda


la página.
 async: significa que el script se ejecutará inmediatamente, pero en
paralelo al análisis del resto de la página.

... y un poco acerca de CSS

CSS, o Cascading Style Sheets, es un lenguaje utilizado junto con HTML


para describir la apariencia de una página y sus elementos. En pocas
palabras, HTML describe la estructura de un documento, mientras que
CSS describe su presentación.

Por ejemplo, en HTML, podemos describir una página que tiene un


encabezado, dos párrafos y una tabla de datos.

En CSS, podemos definir qué fuente se usará en toda la página, qué color
tendrá el fondo o si el cursor del mouse, cuando se mueve sobre la tabla,
debe cambiar de forma.

Entonces podemos tratar CSS como una especie de configuración de la


capa visual de la página. Así, la mayoría de las veces el sitio web se
construirá sobre la base de un archivo HTML (es decir, una descripción de
la estructura), código JavaScript que nos permite agregar, por ejemplo,
algunos mecanismos de interacción y un archivo CSS (que describe la
presentación de la página). Sin embargo, lo importante es que no habrá
página sin un archivo HTML, pero podemos crear fácilmente una página
sin usar archivos CSS. La descripción de CSS en sí está fuera del alcance
del curso actual y la mencionamos solo por orden.

La etiqueta <script>
El código JavaScript que ejecutará el navegador en la página debe
adjuntarse al HTML utilizando la etiqueta <script>, y existen dos formas
de hacerlo. El código se puede incrustar directamente dentro de las
etiquetas <script> y </script>, pero esto solo se recomienda cuando el
código es corto. Otro enfoque es utilizar el atributo "src" para apuntar a un
archivo separado que contiene el código JavaScript. Esto es especialmente
cierto cuando se va a usar el mismo código en varias páginas, porque
repetir exactamente el mismo código muchas veces es una mala práctica,
ya que cualquier cambio debe aplicarse a todos los archivos; y además,
aumenta artificialmente el tamaño de la página. La extensión del archivo
JavaScript es .js.

HTML es leído por el navegador línea por línea, y las etiquetas se ejecutan
justo en el momento en que el navegador analiza la
etiqueta <script> (analizar en lenguajes de programación significa un
análisis formal del código por parte de una máquina para comprender su
estructura). Generalmente las etiquetas <script> se insertan en el
encabezado de la página entre las etiquetas <head> y </head>, y podemos
insertar muchos de ellos en un archivo, por ejemplo, para incluir código
JavaScript de diferentes archivos. Este comportamiento se puede cambiar
para scripts externos señalados por el atributo "src" usando los
atributos "defer" o "async".

 defer: significa que el script debe ejecutarse después de cargar toda


la página.
 async: significa que el script se ejecutará inmediatamente, pero en
paralelo al análisis del resto de la página.

... y un poco acerca de CSS

CSS, o Cascading Style Sheets, es un lenguaje utilizado junto con HTML


para describir la apariencia de una página y sus elementos. En pocas
palabras, HTML describe la estructura de un documento, mientras que
CSS describe su presentación.

Por ejemplo, en HTML, podemos describir una página que tiene un


encabezado, dos párrafos y una tabla de datos.

En CSS, podemos definir qué fuente se usará en toda la página, qué color
tendrá el fondo o si el cursor del mouse, cuando se mueve sobre la tabla,
debe cambiar de forma.

Entonces podemos tratar CSS como una especie de configuración de la


capa visual de la página. Así, la mayoría de las veces el sitio web se
construirá sobre la base de un archivo HTML (es decir, una descripción de
la estructura), código JavaScript que nos permite agregar, por ejemplo,
algunos mecanismos de interacción y un archivo CSS (que describe la
presentación de la página). Sin embargo, lo importante es que no habrá
página sin un archivo HTML, pero podemos crear fácilmente una página
sin usar archivos CSS. La descripción de CSS en sí está fuera del alcance
del curso actual y la mencionamos solo por orden.

¿Cómo podemos ejecutar nuestra código JavaScript?

Comencemos con un ejemplo simple, donde el navegador obtiene una


página simple (quizás incluso vacía) de https://test.org. La dirección es
ficticia para este ejemplo, así que no intentes ingresarla. Mira la figura de
abajo.

hacer clic para agrandar

Empecemos por el lado derecho de la figura. El usuario ejecuta un


navegador web en su computadora (por ejemplo, Chrome). Usando el atajo
de teclado apropiado, se activan las herramientas de desarrollo (ver el
capítulo anterior) para poder usar la consola. Recuerda que estas
herramientas no son necesarias para el uso normal del navegador y, por lo
tanto, están ocultas por defecto. Luego, el usuario
escribe https://test.org (la URL de nuestro sitio falso) en la barra de
direcciones.

En el servidor remoto (lado izquierdo del dibujo), asociado a la


dirección https://test.org, se esta ejecutando un servidor web que, tras
recibir una solicitud de nuestro usuario, preparará una respuesta para
ello. En el caso más simple, la respuesta solo contendrá un archivo html,
que se puede almacenar en el mismo servidor. El archivo html (en este
ejemplo, index.html) se devuelve al usuario y el navegador lo procesa. Si se
define algún contenido (por ejemplo, un párrafo con texto), se mostrará en
la ventana del navegador.

Sin embargo, nos interesa más el hecho de que el archivo index.html


contiene las etiquetas <script> y </script>, con una pieza de código
JavaScript entre ellas. ¿Lo reconoces? Obviamente, esto es un intento de
mostrar nuestro "¡Hola, mundo!" en la consola al cargar la página, se debe
ejecutar el código colocado dentro de las etiquetas <script> y, si las
herramientas para desarrolladores están habilitadas y el panel de la
consola está visible, la consola mostrará "¡Hola, Mundo!".

Como dijimos antes, la etiqueta <script> se puede usar de una manera


diferente, no solo para restringir el lugar donde escribimos código
JavaScript directamente. Si usamos el atributo "src" en esta etiqueta,
podemos indicar un archivo JavaScript separado que se adjuntará aquí.

Revisa el código en la pagina siguiente. En el archivo index.html se


encuentra de nuevo la etiqueta <script>. Esta vez no hay código JavaScript
colocado después, pero al usar el atributo "src", se indica que el código del
archivo main.js debe adjuntarse aquí.

hacer clic para agrandar

Todo funcionará exactamente igual que en el escenario anterior, excepto


que el servidor web proporcionará el archivo main.js además de
index.html. El usuario no notará ninguna diferencia. Por supuesto, colocar
nuestro código en un servidor remoto solo para probarlo sería un poco
engorroso.

Tenemos otra posibilidad, que es que podemos cargar un archivo html


local (es decir, uno que está en nuestra computadora) en el navegador. Si
este código contiene una etiqueta <script> que indica algún archivo
JavaScript, entonces este archivo también se cargará desde los recursos
locales.

La imagen muestra un escenario simple en el que el usuario carga un


archivo index.html local en el navegador, en el que hay una referencia a
main.js (por lo que este archivo también se cargará automáticamente).

hacer clic para agrandar

Puedes cargar un archivo html local escribiendo su ruta local después de


archivo:/// en la barra de direcciones, o simplemente abriéndolo en tu
navegador usando el comando Abrir del menú. Dado que el menú de los
navegadores suele estar oculto, una forma más sencilla puede ser utilizar
un acceso directo para abrir documentos existentes en las aplicaciones. El
atajo es universal, no solo para navegadores y probablemente ya lo hayas
visto:

o en el caso de macOS:
De acuerdo, tal vez finalmente podamos ejecutar algo....

Para ejecutar esto localmente, deberás abrir el editor de código de tu


elección. Crea un nuevo archivo con la extensión .html (el nombre del
archivo no importa, pero es una buena práctica evitar los espacios en el
nombre del archivo). Coloca el siguiente código en este archivo y guárdalo.

<!DOCTYPE html>
<html>
<head>
<title>Empty Page</title>
<script src="main.js"></script>
</head>
<body>
</body>
</html>

Luego, en el mismo editor, crea otro archivo, esta vez llamado main.js (este
es el nombre que usamos en nuestro archivo html). Debe contener una
línea que has visto antes:

console.log("¡Hola, Mundo!");

Guarda los cambios y ve al navegador. Abre una nueva pestaña, habilita


las herramientas de desarrollador (se abren para una pestaña en
particular) y seleccione la herramienta de consola. Toma un momento para
acostumbrarte al diseño de las herramientas de desarrollo (cada
herramienta, incluida la consola, debe colocarse en un panel separado,
que se puede seleccionar).

Y ahora un pequeño desafío. Intenta modificar el archivo html tu mismo


para que no haga referencia al archivo main.js. En su lugar, el mismo
código JavaScript que escribimos en main.js debe colocarse directamente
después de la etiqueta <script>. Si tienes problemas, vuelva al primer
dibujo de esta sección.

Para ejecutar este código en el entorno en línea, colócalo dentro de las


pestañas HTML y, si es necesario, presiona el botón ejecutar.
Next

Ejecutando el código directamente en la consola

Tenemos otra opción bastante conveniente cuando se trata de ejecutar


fragmentos cortos de código JavaScript en el navegador (y nuestro
programa, que consta de una instrucción, es definitivamente corto). Como
dijimos antes, la consola no solo se usa para mostrar información, sino
que también te permite ejecutar instrucciones de JavaScript individuales.
Estas instrucciones deben ejecutarse en el contexto de alguna página
HTML. Sin embargo, no necesariamente tienes que escribir tu página,
como hicimos hace un momento. Intenta abrir una nueva pestaña y
escribe about:blank en la barra de direcciones. Esta es una
pseudodirección que le dice a su navegador que genere y cargue una
página HTML en blanco.

Luego, ejecuta las herramientas de desarrollo. Al principio, podemos


comprobar cómo se ve el HTML generado por el navegador. Para hacerlo,
seleccione la primera herramienta del panel (en Chrome, será Elementos,
en Firefox Inspector). Deberías ver un código html mínimo:

<html>
<head></head>
<body></body>
</html>

Ahora elige la consola de las herramientas para desarrolladores. Deberías


ver un aviso, generalmente un signo > o >> seguido de un cursor
parpadeante (si no hay cursor, has clic en el aviso). Luego puedes ingresar
la instrucción que mostrará "¡Hola, Mundo!" en la consola (usando la
función console.log). El escenario se muestra en la siguiente figura.
hacer clic para agrandar

De hecho, independientemente del navegador, deberíamos obtener el


mismo efecto: la consola mostrará el texto que especificamos. En el caso
de Chrome (que se ejecuta en el sistema operativo Windows), la consola
debería verse así después de completar esta tarea:

hacer clic para agrandar

En el caso de Firefox (también Windows) de esta forma:

hacer clic para agrandar

Para ambos navegadores, las ventanas del depurador que contiene la


consola pueden variar mínimamente según la versión del software y el
sistema operativo que lo ejecute. Las herramientas de desarrollo se pueden
mover. Se pueden ubicar en la parte inferior del navegador, como en los
ejemplos que se muestran, pero también se pueden colocar en el lado
izquierdo o derecho de la ventana (o como una ventana completamente
separada). Así que no te sorprendas si el diseño de tu navegador es
ligeramente diferente al de las imágenes.

Fundamentos de JavaScript 1 (JSE):


Módulo 2

Sección 1

Variables

Temas en esta sección:

 Nombrar, declarar e inicializar variables.


 Declaraciones y modo estricto.
 Cambio de valores en variables.
 Constantes.
 Alcance (bloques, sombreado, y hoisting).

Variables

La capacidad de escribir información diversa en la pantalla, como "¡Hola,


Mundo!" puede ser divertido por un tiempo, pero no es una forma
universal de escribir programas. Es hora de comenzar a aprender más
sobre los elementos del rompecabezas que finalmente te permitirán crear
programas que resuelvan problemas reales.

Existen bastantes de estos elementos, y los presentaremos gradualmente,


aunque no necesariamente en una cronología simple. A menudo
volveremos a lo que ya se ha discutido, ampliando la información anterior
con algo nuevo. A veces también avanzaremos, utilizando mecanismos que
se explicarán con detalle más adelante. Al principio puede parecer un poco
abrumador, pero con el tiempo todo debería comenzar a fusionarse en una
imagen coherente.

El primer elemento de programación del que hablaremos es la variable. Es


posible que conozcas el nombre de una variable de las matemáticas, donde
significa un símbolo que se usa como identificador para diferentes valores
que pueden cambiar. Tienen un papel similar en la programación.

¿Para qué las necesitamos realmente? Como puedes adivinar, la mayoría


de los programas son bastante complejos y rara vez podemos resolver el
problema con una sola operación. Por lo general, el programa constará de
muchas más operaciones, cada una de las cuales puede producir algunos
resultados intermedios, que serán necesarios en los siguientes pasos. Las
variables nos permiten almacenar dichos resultados, modificarlos o
alimentarlos en operaciones posteriores.

Nombrado de variables

Imagína a las variables como contenedores en los que puedes almacenar


cierta información (dicha información se denominará valores de variables).
Cada contenedor debe tener su propio nombre, mediante el cual podremos
indicarlo claramente.

Por lo general, tenemos bastante libertad cuando se trata de inventar estos


nombres, pero recuerda que deben referirse a lo que almacenaremos en la
variable (por ejemplo, altura, color, contador de pasos, etc.). Por supuesto,
JavaScript no verificará la correlación entre el nombre y el contenido de la
variable; es simplemente una de las muchas buenas prácticas que facilitan
que nosotros y otros entendamos el código más adelante.

En la mayoría de los lenguajes de programación, se debe declarar una


variable antes de usarla, y JavaScript no es una excepción. Declarar una
variable es simplemente "reservar" el nombre de la variable. De esta
manera, le informamos al programa que en la parte posterior de la
ejecución, usaremos este nombre para referirnos a nuestro contenedor,
para recuperar un valor de él o guardar un valor en él.

En JavaScript, los nombres de las variables pueden constar de cualquier


secuencia de letras (minúsculas y mayúsculas), dígitos, guiones bajos y
signos de dólar, pero no deben comenzar con un dígito. Hay una lista de
palabras reservadas que no se pueden usar como nombres de variables
(consulta la tabla a continuación).

Lo importante también es que el intérprete de JavaScript distingue entre


minúsculas y mayúsculas, también en nombres de variables, por lo que
nombres como test, Test, o TEST serán tratados como variables diferentes.

Los nombres de las variables en JavaScript pueden ser prácticamente


cualquier cadena de caracteres. Sin embargo, hay un conjunto de palabras
reservadas que no se pueden usar para nombrar variables, funciones o
cualquier otra cosa. Son partes integrales del lenguaje y se les asigna un
significado que no se puede cambiar. A continuación encontrarás una lista
de ellas:
abstract arguments await boolean
break byte case catch
char class const continue
debugger default delete do
double else enum eval
export extends false final
finally float for function
goto implements if import
in instanceof int interface
let long native new
null package private protected
public return short static
super switch synchronized this
throw throws transient true
try typeof var void
volatile while with yield

Declarando variables

Como mencionamos anteriormente, declaramos la variable para reservarle


un nombre. Esto es una simplificación, porque de hecho, el espacio de
memoria también está reservado para la variable, pero al programar en
JavaScript, prácticamente nunca tenemos que pensar en lo que sucede en
la memoria. Por lo general, los valores almacenados en la variable podrán
modificarse durante la ejecución del programa (después de todo, son
"variables"). ¿Por qué? Porque podemos declarar variables cuyos valores no
se pueden cambiar. Para ser honesto, ya ni siquiera las llamamos
variables, las llamamos constantes. Para las declaraciones, usamos las
palabras clave var o let para variables y const para constantes. Por
ahora, sin embargo, sigamos con las variables habituales y volveremos a
las constantes en un momento.

Analicemos el siguiente ejemplo de código (también lo encontrarás en la


ventana del editor; ejecútalo allí y observa los resultados en la consola):

var height;
console.log(height); // -> undefined
console.log(weight); // -> Uncaught ReferenceError: weight is not defined

La primera línea es la declaración de la variable (podemos ver la palabra


clave var). Esta declaración significa que la palabra height sera tratada
como el nombre del contenedor para ciertos valores.
La declaración, como otras instrucciones de JavaScript, debe terminar con
un punto y coma. En la segunda línea, tratamos de escribir el valor de esta
variable (es decir, lo que hay en el contenedor) en la consola. Debido a que
aún no hemos puesto nada allí, el resultado no está definido (el intérprete
conoce esta variable, pero aún no tiene valor, el valor no está definido). En
la siguiente línea, tratamos de imprimir el contenido de la variable
weight... que olvidamos declarar. Esta vez, veremos ReferenceError. El
intérprete de JavaScript, que ejecuta nuestro programa, nos ha informado
que no conoce una variable con este nombre (por lo que la variable en sí
no está definida).

Variables - Declarando variables cont.

En el ejemplo, usamos la palabra clave var. La alternativa es la palabra


clave let. Usamos ambas palabras clave de la misma manera. Ambas están
destinados para declarar variables, y ambas se pueden encontrar en
diferentes ejemplos en Internet o en libros. Sin embargo, no son
exactamente iguales y discutiremos las diferencias en su funcionamiento
más adelante en este capítulo (incluso en varios lugares).

La palabra clave var proviene de la sintaxis JavaScript original, y la


palabra clave let se introdujo mucho más tarde. Por lo tanto, encontrarás
var en programas más antiguos. Actualmente, se recomienda
enfáticamente usar la palabra let por razones que discutiremos en un
momento.

Entonces, echemos un vistazo a nuestro ejemplo reescrito esta vez usando


la palabra clave let.

let height;
console.log(height); // -> undefined

Una de las diferencias básicas en el uso de var y let es que let nos impide
declarar otra variable con el mismo nombre (se genera un error). El uso de
var le permite volver a declarar una variable, lo que puede generar errores
en la ejecución del programa.

var height;
var height;
console.log(height); // -> undefined

El ejemplo anterior demuestra la posibilidad de volver a declarar una


variable usando la palabra clave var. En esta situación, no causará un
error, pero en programas más complejos, una redeclaración, especialmente
por accidente, puede tener consecuencias. Al declarar con let, el intérprete
verifica si dicha variable ya ha sido declarada, sin importar si se usó let o
var en la declaración anterior.

let height;
let height; // -> Uncaught SyntaxError: Identifier 'height' has already been
declared
console.log(height);

Así que usa let para declarar variables, aunque solo sea porque no quieres
volver a declarar accidentalmente una variable.

Inicializando variables

Después de una declaración exitosa, la variable debe ser inicializada, en


otras palabras, debe recibir su primer valor. La inicialización se realiza
asignando un determinado valor a una variable (indicado por su nombre).
Para asignarlo usamos el operador =.

Puedes asignar a una variable: un valor específico; el contenido de otra


variable; o, por ejemplo, el resultado devuelto por una función.
La inicialización se puede realizar junto con la declaración o por separado
como un comando independiente. Es importante ingresar el primer valor
en la variable antes de intentar leerla, modificarla o mostrarla.

let height = 180;


let anotherHeight = height;
let weight;
console.log(height); // -> 180
console.log(anotherHeight); // -> 180
weight = 70;
console.log(weight); // -> 70

En el ejemplo anterior (verifícalo en el editor), las declaraciones de las


variables height y anotherHeight se combinan con su inicialización,
mientras que la variable weight se declara e inicializa por separado. Las
variables height y weight se inicializan proporcionando valores específicos
(más precisamente, un número), mientras que la variable anotherHeight
recibe un valor leído de la variable de height. Los valores de todas las
variables se muestran en la consola.

Por cierto, presta atención a una cosa. Si especificas un nombre de


variable en console.log, el intérprete lo reconoce y muestra su valor. Si
pones el mismo nombre entre comillas, se tratará como texto sin formato y
se mostrará como tal.

let height = 180;


console.log(height); // -> 180
console.log("height"); // -> height

Declaraciones y modo estricto

JavaScript tuvo algunos cambios importantes introducidos en 2009 y


2015. La mayoría de estos cambios ampliaron la sintaxis del lenguaje con
nuevos elementos, pero algunos de ellos se referían solo al funcionamiento
de los intérpretes de JavaScript. A menudo se trataba de aclarar el
comportamiento de los intérpretes en situaciones potencialmente erróneas,
como en los casos de inicialización de variables sin ninguna declaración
previa.

Veamos un ejemplo:

height = 180;
console.log(height); // -> 180
A primera vista, puedes ver que nos hemos olvidado de declarar la variable
height. La sintaxis de JavaScript original permitía tal negligencia, y en el
momento de la inicialización hizo esta declaración por nosotros. Parece
una solución bastante buena, pero desafortunadamente a veces puede
conducir a situaciones ambiguas y potencialmente erróneas (diremos
algunas palabras más al respecto mientras discutimos el alcance).

Vamos a modificar nuestro ejemplo:

"use strict";

height = 180; // -> Uncaught ReferenceError: height is not defined


console.log(height);

Al principio de nuestro código, agregamos "use strict";. Esta sentencia ha


cambiado radicalmente el comportamiento del intérprete. ¿Por qué? Lo
usamos cuando queremos obligar al intérprete a comportarse de acuerdo
con los estándares modernos de JavaScript. Entonces, siempre que no
estés ejecutando un código realmente antiguo, siempre debes usarlo. Y
esta vez, usar una variable sin su declaración anterior se trata como un
error.

La frase "use strict"; debe colocarse al principio del código. Hará que el
intérprete se ocupe del resto del código utilizando el modo estricto, que es
el estándar moderno de JavaScript. Todos los demás ejemplos de nuestro
curso estarán preparados para funcionar en este modo de forma
predeterminada, incluso si "use strict"; no siempre aparezca al principio
del código.

Constantes

La palabra clave const se usa para declarar contenedores similares a


variables. Dichos contenedores se denominan constantes. Las constantes
también se utilizan para almacenar ciertos valores, pero una vez que se
han ingresado valores durante la inicialización, ya no se pueden modificar.
Esto significa que este tipo de contenedor se declara e inicializa
simultáneamente. Por ejemplo, la siguiente declaración de la constante
greeting es correcta:

const greeting = "¡Hola!";


Pero esta próxima definitivamente causa un error:

const greeting; // -> Uncaught SyntaxError: Missing initializer in const


declaration
greeting = "¡Hola!";

Como dijimos, un cambio en la constante es imposible. Esta vez la


declaración es correcta, pero tratamos de modificar el valor almacenado en
la constante.

const greeting = "¡Hola!";


greeting = "Hi!"; // -> Uncaught TypeError: Assignment to constant
variable.

El propósito principal de una constante es erradicar la posibilidad de


cambiar accidentalmente un valor almacenado en ella. Esto es importante
cuando tenemos algunos valores que realmente nunca deberían cambiar.
Los ejemplos típicos de constantes son rutas a recursos, tokens y otros
datos que nunca cambian durante la vida útil del script.

Pero las constantes también se pueden usar como subresultados en los


cálculos o en otros lugares donde la información que se recopiló o calculó
no cambiará más. El uso de una const, además de evitar que un valor se
cambie por error, permite que el motor de JavaScript optimice el código, lo
que puede afectar su rendimiento.

Alcance

Hasta ahora, asumimos que después de declarar una variable, su nombre


podría usarse en todo el código del programa (es decir, el alcance de la
variable es global). Esto no es del todo cierto: el alcance de una variable
depende de dónde se declare. Desafortunadamente, para una buena
comprensión del alcance de una variable, necesitamos aprender algunos
elementos de programación más, como instrucciones o funciones
condicionales, que se analizarán con detalle más adelante en el curso. Así
que aquí nos limitaremos a la información básica y volveremos a este tema
en diferentes partes del curso. Uno de los elementos básicos que influyen
en el alcance de las variables es un bloque del programa.

Bloques del Programa

Podemos separar el código de un programa en bloques. En los bloques que


creamos usando llaves {}, hay un conjunto de instrucciones que, por
alguna razón, deben tratarse de forma independiente. Los bloques suelen
estar asociados a instrucciones condicionales, bucles o funciones, de las
que hablaremos más adelante. También podemos separar un bloque de un
programa que no tenga nada en especial, simplemente eligiendo un
determinado rango de instrucciones (en la práctica, esto no está
especialmente justificado, y por ahora solo lo haremos por motivos
educativos).

Veamos un ejemplo:

let counter;

console.log(counter); // -> undefined

counter = 1;

console.log(counter); // -> 1

counter = counter + 1;

console.log(counter); // -> 2

Primero, declaramos la variable counter. Luego abrimos un bloque dentro


del cual inicializamos esta variable y mostramos su contenido. Fuera del
bloque, aumentamos el valor almacenado en la variable en 1 y lo
mostramos nuevamente. En este caso, el intérprete ejecutará el programa
como si no hubiera notado el bloque, pasando por las instrucciones antes
del bloque, en el bloque y después del bloque. Crear un bloque aquí, sin,
por ejemplo, instrucciones condicionales, no tiene una justificación real, es
solo un ejemplo del uso de llaves {}.

Los bloques del programa se pueden anidar, es decir, podemos crear un


bloque dentro de otro.

let counter;

console.log(counter); // -> undefined

counter = 1;
{

console.log(counter); // -> 1

counter = counter + 1;

console.log(counter); // -> 2

Por cierto, toma en cuenta que el código dentro del bloque se ha movido a
la derecha. Esto se denomina indentación. Para un intérprete de
JavaScript, no importa en absoluto, pero definitivamente aumenta la
legibilidad del código, lo que permite a los lectores (incluso a tí) descubrir
rápidamente qué partes del código están dentro y cuáles están fuera del
bloque. Los editores de código suelen agregar sangrías en los lugares
correctos por sí mismos, pero es un buen hábito recordarlo y, si no
aparecen automáticamente, da formato al código a mano.

Es hora de seguir adelante para determinar qué está pasando realmente


con estos alcances. Desafortunadamente, los alcances de las variables (y
constantes) declaradas con let y const son ligeramente diferentes a las
declaradas con var. Así que los discutiremos de forma independiente.

let y const

La primera regla es simple. Si declaramos alguna variable o constante


usando let o const, respectivamente, fuera de los bloques de código,
serán globales. Con esto queremos decir que sus nombres serán visibles
en todo el programa, fuera de los bloques, dentro de los bloques, en las
funciones, etc. Podremos referirnos a ellos en cualquier lugar por sus
nombres y, por supuesto, tendremos acceso a sus valores.

¿Qué sucede si declaramos algo usando let o const dentro de un bloque?


Esto creará una variable o constante local. Será visible solo dentro del
bloque en el que se declaró y en los bloques que opcionalmente se pueden
anidar en él.

Veamos un ejemplo sencillo:


let height = 180;
{
let weight = 70;
console.log(height); // -> 180
console.log(weight); // -> 70
}
console.log(height); // -> 180
console.log(weight); // -> Uncaught ReferenceError: weight is not defined

La variable height, declarada fuera del bloque, es global. La variable weight


es local: su alcance está limitado por el bloque en el que se declaró. Esto
es claramente visible cuando se intenta mostrar los valores de ambas
variables dentro y fuera del bloque. También podemos probar el caso con
bloques anidados

let height = 200;


{
let weight = 100;
{
let info = "tall";
console.log(height); // -> 200
console.log(weight); // -> 100
console.log(info); // -> tall
}
console.log(height); // -> 200
console.log(weight); // -> 100
console.log(info); // -> Uncaught ReferenceError: info is not defined
}

Como puedes ver, la variable info declarada en el bloque más interno solo
es visible dentro de él. La variable weight es visible tanto dentro del bloque
en el que fue declarada como dentro del bloque anidado en ella. Y la
variable global height es visible en todas partes.

Simple, ¿no?

var

En el caso de declaraciones de variables usando la palabra clave var, la


situación es ligeramente diferente. La variable declarada utilizándola fuera
de los bloques será, como en el caso de let, global, es decir, será visible en
todas partes. Si la declaras dentro de un bloque, entonces... bueno, por lo
general volverá a ser global.
Comencemos con un ejemplo simple:

var height = 180;


{
var weight = 70;
console.log(height); // -> 180
console.log(weight); // -> 70
}
console.log(height); // -> 180
console.log(weight); // -> 70

Como era de esperar, ambas variables, height y weight, resultan ser


globales. ¿Las variables declaradas usando var siempre,
independientemente del lugar de declaración, serán globales?
Definitivamente no. El problema es que var ignora los bloques de programa
ordinarios, tratándolos como si no existieran. Entonces, ¿en qué situación
podemos declarar una variable local usando var? Sólo dentro de una
función. Dedicaremos mucho espacio a discutir la función y luego
volveremos al problema del alcance de la variable. Ahora intentaremos
presentar y discutir solo un ejemplo simple, que mostrará que las
variables var a veces también son locales.

Unas breves palabras sobre funciones

Empecemos explicando qué son las funciones. A menudo sucede que una
determinada pieza de código, que realiza alguna tarea específica, se
utilizará muchas veces. Sí, puedes copiar este fragmento de código, todas
sus instrucciones, en cualquier lugar donde desees utilizarlo. Sin embargo,
esto sería muy ineficiente. En primer lugar, el tamaño de nuestro
programa crecería innecesariamente. En segundo lugar, si quisiéramos
hacer algunos cambios en este código, por ejemplo, para corregir algún
error, tendríamos que hacerlo en todos los lugares donde lo usamos.

Una solución simple a esto problema es una función. Una función es solo
una pieza de código separada que nombras, de la misma manera que
nombras una variable. Si deseas usarla en algún lugar, simplemente se
hace referencia a ella con ese nombre (decimos que llamamos a la función).

La declaración de una función simple, digamos testFunction, puede verse


así:

function testFunction() {
console.log("Hola");
console.log("Mundo");
}

La forma de definir la función que se muestra en el ejemplo es una de


varias disponibles en JavaScript. La definición comienza con la palabra
clave function, seguida del nombre de la función que inventamos. Después
del nombre, verás paréntesis, que opcionalmente podrían contener
parámetros pasados a la función (volveremos a esto cuando analicemos
funciones con más precisión). Luego abrimos el bloque del programa, que
contiene las instrucciones pertenecientes a la función. Al definir una
función, las instrucciones contenidas en la función no se ejecutan. Para
ejecutar la función, debes llamarla de forma independiente, usando su
nombre.

Echa un vistazo al siguiente programa.

console.log("Comencemos:"); // -> Comencemos:


console.log("Hola"); // -> Hola
console.log("Mundo"); // -> Mundo
console.log("y otra vez:"); // -> y otra vez:
console.log("Hola"); // -> Hola
console.log("Mundo"); // -> Mundo
console.log("y una vez más:"); // -> y una vez más:
console.log("Hola"); // -> Hola
console.log("Mundo"); // -> Mundo

Imprimirá una secuencia de texto en la consola:

Comencemos:
Hola
Mundi
y otra vez:
Hola
Mundo
y una vez más:
Hola
Mundo
Podemos reescribir el mismo programa usando nuestra
función testFunction. Declarémoslo de nuevo y llamémosla en los lugares
correctos:

function testFunction() {
console.log("Hola");
console.log("Mundo");
}

console.log("Comencemos:");
testFunction();
console.log("y otra vez:");
testFunction();
console.log("y una vez más:");
testFunction();

salida

El efecto del programa será el mismo que antes (prueba ambos ejemplos).

La palabra clave var - continuación

Después de esta breve introducción a las funciones (obviamente, esta no


es nuestra última reunión con ellas), volvamos a la palabra clave var y los
ámbitos de las variables.

Si declaramos una variable usando la palabra clave var dentro de una


función, su alcance se limitará solo al interior de esa función (es un
alcance local). Esto significa que el nombre de la variable se reconocerá
correctamente solo dentro de esta función.

Consideremos el siguiente ejemplo:

var globalGreeting = "Buenos";

function testFunction() {
var localGreeting = "Días";
console.log("función:");
console.log(globalGreeting);
console.log(localGreeting);
}

testFunction();

console.log("programa principal:");
console.log(globalGreeting);
console.log(localGreeting); // -> Uncaught ReferenceError: localGreeting is
not defined

En primer lugar, ejecuta este programa y observa los resultados en la


consola. ¿Qué pasó y, sobre todo, por qué pasó?

Echemos un vistazo más de cerca al código. En el ejemplo, declaramos la


variable global globalGreeting. Luego definimos la función testFunction,
dentro de la cual declaramos la variable local localGreeting. Luego
llamamos a la función testFunction, que resultó en escribir los valores de
ambas variables (dentro de la función, tenemos acceso tanto a la variable
global como a las locales). Intentar acceder a la variable
local localGreeting fuera de la función fallará. Así que finalmente logramos
demostrar que las declaraciones de variables que usan la
palabra var también pueden ser locales.

Sombreado

JavaScript permite sombreado de variables. ¿Que significa eso? Significa


que podemos declarar una variable global y una variable local con el
mismo nombre.

En el alcance local, en el que declaramos una variable local usando su


nombre, tendremos acceso al valor local (la variable global está oculta
detrás de la local, por lo que no tenemos acceso a ella en este alcance
local). Usar este nombre fuera del alcance local significa que nos
referiremos a la variable global. Sin embargo, esta no es la mejor
práctica de programación y debemos evitar tales situaciones. No es
difícil adivinar que con un poco de falta de atención, el uso de este
mecanismo puede conducir a situaciones no deseadas y probablemente a
errores en el funcionamiento del programa.

Si queremos evitar tales situaciones, sería bueno ver exactamente de qué


se trata. Comencemos con un ejemplo sin sombreado:

let counter = 100;


console.log(counter); // -> 100
{
counter = 200;
console.log(counter); // -> 200
}
console.log(counter); // -> 200
La variable counter, declarada al principio del programa, es una variable
global. A lo largo del programa, también dentro del bloque, operamos sobre
esta misma variable. Un pequeño cambio en el código es suficiente para
que el programa se comporte de manera completamente diferente.

let counter = 100;


console.log(counter); // -> 100
{
let counter = 200;
console.log(counter); // -> 200
}
console.log(counter); // -> 100

¿Ves la diferencia? Esta vez en el bloque, en lugar de counter = 200; (es


decir, cambios en el contenido de la variable counter global), aparece la
instrucción let counter = 200; (es decir, declaraciones de la variable local
combinada con su inicialización). El intérprete consideraría que tal
situación es incorrecta si la redeclaración apareciera en el mismo alcance.

Sin embargo, la declaración es local (tiene un alcance diferente al global) y


todas las referencias a la variable con este nombre dentro del bloque se
referirán a esta variable local. Fuera del bloque, la variable global se
seguirá viendo con el mismo nombre. Presta atención a los valores que
muestra la consola.

Sombreado - continuación

El sombreado no solo puede estar relacionado con la situación en la que


una variable local cubre una variable global. Si aparecen alcances
anidados (por ejemplo, bloques anidados en el caso de una declaración
let), la variable local declarada en un bloque más anidado eclipsará la
variable local del mismo nombre declarada en el bloque externo.

El sombreado también está presente en las declaraciones de variables que


usan la palabra var, y esta vez el alcance local no está limitado por el
bloque de programa, sino por el bloque de funciones.

var counter = 100;

function testFunction() {
var counter = 200;
console.log(counter);
}

console.log(counter); // -> 100


testFunction(); // -> 200
console.log(counter); // -> 100

En la mayoría de los casos, esto no es deseable, así que trate de evitar dar
los mismos nombres de variable a varias variables, independientemente de
dónde las declares.

Hoisting

¿Recuerdas que dijimos que todas las variables deben declararse antes de
su uso? Esto no es del todo cierto, y realmente la palabra "debería" encaja
mejor que "debe". Por supuesto, una buena práctica es siempre declarar
las variables antes de usarlas. Y apégate a esto. Pero la sintaxis de
JavaScript original permite algunas desviaciones de esta regla.

El intérprete de JavaScript escanea el programa antes de ejecutarlo,


buscando errores en su sintaxis, entre otras cosas. Hace una cosa más en
esta ocasión. Busca todas las declaraciones de variables y las mueve al
principio del rango en el que fueron declaradas (al principio del programa
si son globales, al principio del bloque si es una declaración let local, o al
principio de la función si es una declaración local var). Todo esto sucede,
por supuesto, en la memoria del intérprete, y los cambios no son visibles
en el código.

Hoisting, es un mecanismo bastante complejo y francamente bastante


incoherente. Comprenderlo bien requiere la capacidad de usar libremente
muchos elementos de JavaScript, que aún no hemos mencionado.

Además, es más una curiosidad que algo práctico que usarás al escribir
programas, por lo que veremos solo un pequeño ejemplo que nos permitirá
comprender aproximadamente su principio. Esto puede facilitarte la
comprensión de algunas situaciones sorprendentes al escribir tu propio
código o probar ejemplos que encuentres en varias fuentes.

var height = 180;


console.log(height); // -> 180
console.log(weight); // -> Uncaught ReferenceError: weight is not defined
En el ejemplo anterior, olvidamos declarar la variable weight. El resultado
es obvio: nos estamos refiriendo a una variable (es decir, estamos tratando
de leer su contenido) que no existe. Algo como esto debe terminar en un
error.

Hagamos un pequeño cambio:

var height = 180;


console.log(height); // -> 180
console.log(weight); // -> undefined
var weight = 70;
console.log(weight); // -> 70

Esta vez declaramos nuestra variable, pero en un lugar bastante extraño.


Junto con la declaración, también realizamos la inicialización. El resultado
del programa puede ser un poco sorprendente. Esta vez no hay errores. El
Hoisting ha funcionado y el intérprete ha movido la declaración al
comienzo del rango (en este caso, el programa).

Sin embargo, el intento de mostrar el contenido de la variable weight da


dos resultados diferentes. ¿Por qué? El hositing solo se refiere a la
declaración, no a la inicialización. Entonces el valor 70, que le asignamos a
la variable weight, permanece en la línea donde está la declaración
original. El motor de JavaScript interpreta el ejemplo anterior más o
menos de la siguiente manera:

var weight;
var height = 180;
console.log(height); // -> 180
console.log(weight); // -> undefined
weight = 70;
console.log(weight); // -> 70

Desafortunadamente, el hoisting funciona un poco diferente con las


declaraciones let y const.

Sin embargo, no entraremos en eso. Basta con que seas consciente del
fenómeno. Y sobre todo, recordar SIEMPRE declarar las variables antes de
usarlas.

Resumen
Usar variables, es decir, declarar, inicializar, cambiar o leer sus valores es
una parte elemental de prácticamente todos los lenguajes de
programación. JavaScript no es la excepción, ya que necesitas usar
variables para programar en él. Recuerda declarar las variables antes de
usarlas. Presta atención en dónde las declaras, ya sean locales o globales.
Trata de usar las palabras clave let y const, no la palabra var. Saber esto
último será útil no solo para comprender los ejemplos que se encuentran
en varias fuentes, sino para que puedas evitar hacer lo mismo. Recuerda
no usar los mismos nombres para diferentes variables, incluso si las
declaras en diferentes rangos. Y, por supuesto, asigna nombres a las
variables que estarán relacionadas con lo que deseas almacenar en ellas:
el código debe ser legible no solo para el intérprete, sino también para las
personas.

Tareas

Tarea 1

Juguemos a la floristería. Declara seis variables, recordando nombrarlas


según su propósito:

 el precio de una sola rosa (8) y el número de rosas que tienes (70)
 el precio de un solo lirio (10) y el número de lirios que tienes (50)
 el precio de un solo tulipán (2) y la cantidad de tulipanes que tienes
(120)

Ahora declara tres variables, una para cada una de las rosas, lirios y
tulipanes que tienes, en las que colocas su precio total. Inserta los valores
correspondientes en las variables utilizando las variables declaradas en el
paso anterior. Finalmente, declara una variable en la que almacenes el
precio de todas tus flores (nuevamente, usa las variables anteriores para la
inicialización). Muestra toda la información del inventario en la consola de
la siguiente forma:

Rosa: precio unitario: 8 , cantidad: 70 , valor: 560


Lirio: precio unitario: 10 , cantidad: 50 , valor: 500
Tulipán: precio unitario: 2 , cantidad: 120 , valor: 240
Total: 1300

Tarea 2
Modifica el código del ejemplo anterior. Supón que los precios de las flores
serán constantes (no cambiarán). Declara e inicializa las variables
restantes de la misma manera que en el ejemplo anterior. Muestra toda la
información recopilada en la consola. Ahora disminuye el número de rosas
en 20 y el de lirios en 30. Vuelve a mostrar toda la información recopilada
en la consola.

Tipos de Datos y sus Conversiones

Los programas están destinados a procesar datos. No importa si se trata


de una aplicación de tienda web, un sistema de gestión de recursos
humanos o un juego de computadora, cada uno de estos programas lee,
procesa y almacena grandes cantidades de datos. En el capítulo anterior,
ya aprendimos cómo declarar, iniciar y modificar las variables que
permiten almacenar estos datos en el contexto de un programa en
ejecución. Mientras discutíamos las variables, apareció el concepto de
tipos de datos y resultó que el lenguaje JavaScript, entre otras cosas, te
permite cambiar el tipo de dato almacenado en una variable.

Podemos dividir los datos según sus propiedades. Por ejemplo,


seguramente distinguirás intuitivamente entre datos numéricos y datos de
texto. Tal clasificación es, por supuesto, arbitraria. Los números se
pueden dividir en, por ejemplo, números enteros y números reales.

Distinguir los datos por sus tipos es uno de los rasgos característicos de
cualquier lenguaje de programación. Cada tipo de dato está conectado con
ciertas operaciones que podemos realizar sobre él. Por lo general, también
existen métodos para convertir datos entre tipos seleccionados (por
ejemplo, un número se puede convertir para que se guarde como una
cadena).

En JavaScript, los tipos de datos se dividen en primitivos (o simples)


y complejos (o compuestos). Entre los tipos primitivos podemos
encontrar números y cadenas de caracteres, mientras que los tipos
complejos incluyen, por ejemplo, arreglos y objetos.

La diferencia entre estos tipos de datos se encuentra precisamente en sus


nombres. Los tipos primitivos, bueno, simplemente no son complejos. Si
escribes datos de un tipo primitivo en una variable, se almacenará allí un
valor particular. Este valor será atómico, es decir, no será posible extraer
componentes de él. Los datos de tipos complejos, como un arreglo,
constarán de muchos elementos de tipos primitivos (no complejos).

Por lo tanto, lógicamente nos ocuparemos primero de los tipos primitivos.


Antes de pasar a discutir los tipos de datos, debemos presentar un nuevo
concepto más: literales.

Los literales son una forma de anotar valores específicos (datos) en el


código del programa. Los literales se encuentran en prácticamente todos
los lenguajes de programación, incluido JavaScript. Usamos literales en el
capítulo anterior al inicializar variables.

Veamos un ejemplo:

let year = 1990;


console.log(year); // -> 1990
console.log(1991); // -> 1991
console.log("Alice"); // -> Alice

En este ejemplo, declaramos la variable year e inmediatamente la


iniciamos con el valor 1990. Los dígitos 1990, escritos en el código en el
lugar de inicialización de la variable, son un literal que representa
un número . El valor 1990 se muestra en la consola utilizando la variable
year. Luego mostramos en la consola el valor 1991 y "Alice", en ambos
casos usando literales (que representan un número y
una cadena respectivamente). En JavaScript, casi cada tipo de datos tiene
su propio literal.

El operador typeof

Mientras aprendes sobre los tipos de datos de JavaScript, el


operador typeof puede ser útil. De hecho, también es útil para el trabajo
normal con este lenguaje, por lo que sería bueno que lo recordaras para
más adelante. Uno de los capítulos posteriores lo dedicaremos a
los operadores, pero en este punto es suficiente saber que un operador es
un símbolo o nombre que representa alguna acción a realizar sobre los
argumentos indicados. Por ejemplo, el símbolo + es un operador de dos
argumentos que representa la suma.

El operador typeof que acabamos de mencionar es unario (solo toma un


argumento) y nos informa del tipo de datos indicados como un argumento
dado. El argumento puede ser un literal o una variable; en este último
caso, se nos informará sobre el tipo de datos almacenados en él. El
operador typeof devuelve una cadena con uno de los valores fijos
asignados a cada uno de los tipos.

Todos los posibles valores de retorno del operador typeof son:

"undefined"
"object"
"boolean"
"number"
"bigint"
"string"
"symbol"
"function"

salida

Esta lista nos muestra aproximadamente qué tipos de datos trataremos en


JavaScript.

Probemos el operador typeof usando un ejemplo simple:

let year = 1990;


console.log(typeof year); // -> number
console.log(typeof 1991); // -> number

let name = "Alice";


console.log(typeof name); // -> string
console.log(typeof "Bob"); // -> string

let typeOfYear = typeof year;


console.log(typeOfYear); // -> number
console.log(typeof typeOfYear); // -> string

Nuevamente declaramos e iniciamos la variable year. Como puedes ver,


typeof tanto para el literal 1991 como para la variable que contiene un
número (la inicializamos con el literal 1990) devolverá la palabra "number".
Realizamos una prueba similar en las cadenas "Alice" y "Bob", usando el
nombre de la variable. Además, hacemos un pequeño experimento. El
resultado de typeof year se almacena en la variable denominada
typeOfYear. Como puede ver, almacena el valor como un "number". Si
comprobamos el tipo de esta variable, obtenemos "string". Comprueba el
ejemplo tu mismo en el editor.

Tipos de Datos Primitivos


En JavaScript, hay seis tipos de datos primitivos (o simples): Boolean,
Number, BigInt, String, Symbol y undefined. Además, el valor
primitivo null también se trata como un tipo separado. Un dato primitivo,
como ya hemos dicho, es un tipo de dato cuyos valores son atómicos. Esto
significa que el valor es un elemento indivisible.

Intentemos echar un vistazo más de cerca a los datos primitivos.

Boolean

El Boolean (booleano) es un tipo de dato lógico. Solo puede tomar uno de


dos valores: true o false. Se usa principalmente como una expresión
condicional necesaria para decidir qué parte del código debe ejecutarse o
cuánto tiempo debe repetirse algo (esto se denomina declaración de flujo
de control y lo veremos más de cerca en el Módulo 4).

Los valores booleanos también se utilizan como lo que comúnmente se


conoce como una bandera, una variable que señala algo que puede estar
presente o ausente, habilitado o deshabilitado, etc. Como cualquier otra
variable, los valores booleanos deben tener nombres claros e informativos.
No es obligatorio, pero a menudo podemos ver que los nombres de las
banderas booleanas tienen el prefijo "es", para mostrar la intención de
comprobar si hay valores verdaderos o falsos.

let isDataValid = true;

let isStringTooLong = false;

let isGameOver = false;

continueLoop = true;

console.log(false); // -> false

console.log(typeof false); // -> boolean

console.log(isDataValid); // -> true

console.log(typeof isDataValid); // -> boolean


Podemos realizar, sin conversión (es decir, cambiar a otro tipo) operaciones
lógicas en valores booleanos, algunos quizás los conozcas de las
matemáticas, como NOT, AND y OR (los símbolos !, & & y ||
correspondientemente). Sabremos más sobre ellos en el capítulo de
operadores.

Number (numérico)

Este es el tipo numérico principal en JavaScript que representa tanto


números reales (por ejemplo, fracciones) como enteros. El formato en el
que se almacenan los datos de este tipo en la memoria hace que los
valores de este tipo sean a veces aproximados (especialmente, pero no
únicamente, valores muy grandes o algunas fracciones). Se supone, entre
otras cosas, que para garantizar la exactitud de los cálculos, los valores
enteros deben limitarse en JavaScript al rango de -(253 – 1) a (253 – 1).

Los números permiten todas las operaciones aritméticas típicas, como


suma, resta, multiplicación y división.

const year = 1991;


let delayInSeconds = 0.00016;
let area = (16 * 3.14);
let halfArea = area / 2;

console.log(year); // -> 1991;


console.log(typeof year); // -> number;

Los números en JavaScript generalmente se presentan en forma decimal, a


la que estamos acostumbrados en la vida cotidiana. Sin embargo, si un
literal que describe un número está precedido por un prefijo apropiado,
podemos presentarlo en hexadecimal (0xâ¦)€ , octal (0o...) o binario (0b...).
También podemos escribir números en forma exponencial, por ejemplo, en
lugar de 9000, podemos escribir 9e3, y en lugar de 0.00123, podemos
escribe 123e-5. Probablemente ya estés familiarizado con los términos que
usamos hace un momento, como representación decimal, hexadecimal o
exponencial.

let a = 10; // decimal - default


let b = 0x10; // hexadecimal
let c = 0o10; // octal
let d = 0b10; // binary

console.log(a); // -> 10
console.log(b); // -> 16
console.log(c); // -> 8
console.log(d); // -> 2

let x = 9e3;
let y = 123e-5;
console.log(x); // -> 9000
console.log(y); // -> 0.00123

Además de los números regulares en JavaScript, usamos tres valores


especiales adicionales, que son: Infinity, -Infinity y NaN (no un número).
Los dos primeros no requieren ninguna explicación adicional: son
exactamente lo que sabemos de las matemáticas. El último, NaN, no es
tanto un valor numérico como una notificación de que alguna acción
aritmética (o función matemática) no se pudo realizar porque el argumento
no es un número o no se puede convertir a un número.

let a = 1 / 0;
let b = -Infinity;

console.log(a); // -> Infinity


console.log(b); // -> -Infinity
console.log(typeof a); // -> number
console.log(typeof b); // -> number

let s = "definitivamente no es un numero";


let n = s * 10;
console.log(n); // -> NaN
console.log(typeof n); // -> number

Prueba estos ejemplos e intenta cambiar los valores que aparecen en ellos
tu mismo.

BigInt

El tipo de dato BigInt no se usa con demasiada frecuencia. Nos permite


escribir números enteros de prácticamente cualquier longitud. Para casi
todas las operaciones numéricas normales, el tipo Number es suficiente,
pero de vez en cuando necesitamos un tipo que pueda manejar números
enteros mucho más grandes.
Podemos usar operaciones matemáticas en BigInts de la misma manera
que en Numbers, pero hay una diferencia al dividir. Como BigInt es un tipo
entero, el resultado de la división siempre se redondeará hacia abajo al
número entero más cercano.

Los literales BigInt son números con el sufijo …n.

let big = 1234567890000000000000n;


let big2 = 1n;

console.log(big); // -> 1234567890000000000000n


console.log(typeof big); // -> bigint

console.log(big2); // -> 1n
console.log(7n / 4n); // -> 1n

No puedes usar otros tipos de datos en operaciones aritméticas con


BigInts, es decir, no puedes agregar un BigInt y un Number entre sí (esto
generará un error).

let big3 = 1000n + 20;


// -> Uncaught TypeError: Cannot mix BigInt and other types, use explicit
conversions

BigInt no tiene su propio equivalente de los valores Infinity o NaN. En el


caso del tipo Number, dichos valores aparecen al dividir entre 0 (resultado
Infinito) o al intentar realizar una acción aritmética sobre un valor que no
es un número (resultado NaN). En el caso del tipo BigInt, dichas acciones
generarán un error.

let big4 = 1000n / 0n; // -> Uncaught RangeError: Division b

String (cadenas)

El tipo String representa una secuencia de caracteres que forman un


fragmento de texto. Las operaciones comunes en los textos incluyen la
concatenación, la extracción de la subcadena y la verificación de la
longitud de la cadena. Las cadenas se utilizan mucho en la programación
y más aún en el desarrollo web, ya que tanto HTML como gran parte del
contenido de Internet es texto.
El uso más común de texto en el desarrollo web incluye:

 Enlaces y rutas a recursos.


 Tokens.
 Comprobación de formularios y entradas llenadas por el usuario.
 Generación de contenido dinámico.

Las Cadenas, como otros datos primitivos, son inmutables, por lo que
cuando queremos cambiar incluso una letra en una cadena, en realidad,
creamos una nueva cadena.

En ejemplos anteriores, ya usamos cadenas de caracteres. Usamos


comillas para indicar que un texto determinado debe tratarse como una
cadena (es decir, tipo String). Los literales de cadena se pueden crear
utilizando comillas simples o dobles, siempre que coincidan los caracteres
de comillas inicial y final.

let country = "Malawi";


let continent = 'Africa';

console.log(country); // -> Malawi


console.log(typeof country); // -> string
console.log(continent); // -> Africa
console.log(typeof continent); // -> string

Si usas comillas dobles para marcar una cadena, puedes colocar comillas
simples dentro de la cadena y se tratarán como caracteres normales. Esto
también funcionará en la situación opuesta (es decir, colocar comillas
dobles entre comillas simples).

let message1 = "El buque 'Mars' hizo escala en el puerto.";


let message2 = 'El ciclón "Cilida" pasará cerca de Mauritius.';

console.log(message1); // -> El buque 'Mars' hizo escala en el puerto.


console.log(message2); // -> El ciclón "Cilida" pasará cerca de Mauritius.

Si deseas poner comillas simples o dobles dentro de la cadena, también


puedes usar el carácter de escape: barra invertida. Una comilla precedida
por \ (barra invertida) se interpretará como caracteres ordinarios que son
parte de nuestra cadena, no partes de una construcción literal. La barra
invertida en sí misma, si se va a tratar como un carácter ordinario (no
como un carácter de control), también debe estar precedida por... un
carácter de escape (es decir, una barra invertida).

let message1 = 'El buque \'Mars\' hizo escala en el puerto.';


let message2 = "El ciclón \"Cilida\" pasará cerca de Mauritius.";

console.log(message1); // -> El buque 'Mars' hizo escala en el puerto.


console.log(message2); // -> El ciclón "Cilida" pasará cerca de Mauritius.

let path = "C:\\Windows";


console.log(path); // -> C:\Windows

Intentar realizar operaciones aritméticas en valores de tipo String, como


resta, multiplicación o división, por lo general terminará en un error. Más
precisamente, el valor de NaN se devolverá como resultado de la acción.

¿Por qué sucede esto? Al ver los operadores aritméticos -, * o \, el


intérprete de JavaScript intenta interpretar los valores dados como
números, o convertirlos en números. Entonces, si las cadenas de
caracteres consisten en dígitos, la conversión automática será exitosa y
obtendremos el resultado de la acción aritmética como un valor de tipo
Number. Si la cadena de caracteres no puede interpretarse como un
número (y convertirse), obtendremos el resultado NaN. Hablaremos más
sobre las conversiones en un momento.

let path = "C:\\Windows" - "Windows";


console.log(path); // -> NaN

let test = "100" - "10";


console.log(test); // -> 90
console.log(typeof test); // -> number

La excepción es la operación de suma, que no se tratará como una


operación aritmética, sino como un intento de crear una nueva cadena
mediante la combinación de dos cadenas o más.

let path = "C:\\" + "Windows";


console.log(path); // -> C:\Windows

let test = "100" + "10";


console.log(test); // -> 10010
console.log(typeof test); // -> string
Un mecanismo muy conveniente que se introdujo en JavaScript en 2015
es la interpolación de cadenas. Te permite tratar una cadena de
caracteres como una plantilla, en la que puedes colocar valores en lugares
seleccionados, como los que se toman de las variables. Tal literal se crea
usando acentos graves en lugar de comillas. Los lugares donde se insertan
los valores están marcados con corchetes precedidos por un signo de $.

let country = "Malawi";


let continent = "Africa";

let sentence = ` ${country} se ubica en ${continent}.`;


console.log(sentence); // -> Malawi se ubica en Africa.

Puedes hacer mucho trabajo útil con datos del tipo String.
Desafortunadamente, requieren dos nuevos conceptos: métodos (e
indirectamente, objetos) y autoboxing. La explicación exacta de ambos
conceptos va más allá del alcance de este curso, por lo que intentaremos
simplificarlos un poco.

En uno de los capítulos anteriores, presentamos el concepto de función,


también en una forma algo simplificada. Ahora hablemos de métodos.

Un método es un tipo especial de función que pertenece a un objeto.


Los objetos son tipos de datos complejos, que pueden constar de muchos
valores (almacenados en propiedades) y métodos. Si deseas llamar al
método de un objeto, escribe el nombre del método después de un punto.
¿Esto te recuerda a algo? Esta es exactamente la notación que usas
cuando llamas a console.log. El objeto consola tiene muchos otros métodos
además del método log, como time y timeEnd (que se pueden usar para
medir el tiempo).

console.time();
console.log("probar consola"); // -> probar consola
console.timeEnd(); // -> default: 0.108154296875 ms

Todos los datos de tipos primitivos como Number, BigInt, Boolean o String
tienen objetos correspondientes a los que se pueden convertir. Cada uno
de estos objetos tendrá métodos diseñados para un tipo de dato específico.
Llegados a este punto, llegamos a otro concepto, el autoboxing. Si aparece
un punto después de un literal que representa un tipo primitivo, o después
de una variable que contiene este tipo de datos, el intérprete de JavaScript
intenta tratar este valor como un objeto y no como un primitivo. Para este
propósito, convierte al dato primitivo en el objeto correspondiente sobre la
marcha, que tiene los métodos apropiados (es decir, realiza autoboxing).
Un poco confuso, ¿no? Afortunadamente, para usar métodos, no tenemos
que entenderlo exactamente, es suficiente seguir la convención dada.

Echemos un vistazo a un ejemplo:

let river = "Mekong";


let character = river.charAt(2);
console.log(character); // -> k

En la variable river, almacenamos el dato primitivo de tipo String. En la


siguiente línea, nos referimos a esta variable, escribiendo un punto
después de su nombre y el nombre de uno de los métodos: charAt (un
método del objeto de la clase String). Aunque el dato primitivo no contiene
métodos a los que se pueda llamar, el intérprete convierte temporalmente
este valor en un objeto adecuado que ya tiene dichos métodos. Uno de
estos métodos es charAt, al que ahora llamamos. El método opera en una
cadena colocada originalmente en la variable river y devuelve una sola
letra desde la posición especificada (las letras se cuentan a partir de 0).

Una vez completada la operación, el intérprete elimina el objeto temporal.


Entonces, desde nuestro punto de vista, parece que acabamos de llamar a
un método en un tipo de dato primitivo.

Undefined

El tipo undefined (indefinido) tiene un solo valor: undefined. Es el valor


predeterminado que tienen todas las variables después de una declaración
si no se les asigna ningún valor. También puedes asignar el
valor undefined a cualquier variable, pero en general, esto debe evitarse,
porque si necesitamos marcar una variable para que no tenga ningún
valor significativo, debemos usar null.

let declaredVar;
console.log(typeof declaredVar); // -> undefined

declaredVar = 5;
console.log(typeof declaredVar); // -> number
declaredVar = undefined;
console.log(typeof declaredVar); // -> undefined

//El operador typeof también puede devolver el valor indefinido cuando se


usa una variable inexistente como argumento.

console.log(typeof notDeclaredVar); // -> undefined


console.log(notDeclaredVar); // -> Uncaught ReferenceError: notDeclared
is not defined

Symbol

El tipo de dato Symbol es, bueno... complicado por decir lo menos. Y,


afortunadamente, no nos resulta especialmente útil.

Es un nuevo tipo primitivo que se agregó a JavaScript en 2015. No tiene


ningún valor literal y solo se puede crear mediante una función de
constructor especial. Los símbolos son una forma de identificador que
garantiza que sea único.

Los símbolos son un tema avanzado, y para comprender su poder y


utilidad, tendremos que cubrir muchos otros temas primero, así que por
ahora, recuerda que el tipo de dato Symbol existe.

null

El valor null es bastante específico. El valor en sí es primitivo, mientras


que el tipo al que pertenece no es un tipo primitivo, como Number o
undefined. Esta es una categoría separada, asociada con tipos complejos,
como objetos. El valor null se usa para indicar que la variable no contiene
nada y, en la mayoría de los casos, es una variable que pretende contener
valores de tipos complejos.

En pocas palabras, podemos suponer que el valor undefined se asigna


automáticamente a las variables no inicializadas, pero si queremos indicar
explícitamente que la variable no contiene nada, le asignamos un
valor null. Una advertencia importante para null es que cuando se verifica
con el operador typeof, devolverá "object", un resultado sorprendente. Esto
es parte de un sistema de objetos mucho más complicado, pero por ahora,
solo necesita saber que typeof null es igual a "object".

let someResource;
console.log(someResource); // -> undefined
console.log(typeof someResource); // -> undefined

someResource = null;
console.log(someResource); // -> null
console.log(typeof someResource); // -> object

En este curso, sin embargo, aparte de menciones menores, no estaremos


aprendiendo un concepto conocido como programación orientada a
objetos, y por lo tanto, usar el valor null no será tan importante para
nosotros por el momento.

Conversiones de Tipo de Datos

Funciones de construcción primitivas

Usar literales no es la única manera de crear variables de los tipos


primitivos dados. La segunda opción es crearlos usando funciones del
tipo constructor. Este tipo de funciones se utilizan principalmente en
JavaScript para la programación orientada a objetos, que está fuera del
alcance de nuestro curso. Sin embargo, estas pocas funciones
constructoras enumeradas también se pueden usar para crear datos
primitivos, no solo objetos (esta no es una característica general, sino solo
para las funciones enumeradas). Las siguientes funciones devolverán
datos primitivos de un tipo
determinado: Boolean, Number, BigInt y String.

La mayoría de estas funciones se pueden llamar sin argumentos. En tal


situación:

 La función String por defecto creará y devolverá una cadena vacía


primitiva: "".
 La función Number por defecto creará y devolverá el valor 0;
 La función Boolean por defecto creará y devolverá el valor de false.

La función BigInt, a diferencia de otras funciones constructoras, requiere


que le pases algún valor inicial. Puede ser un número entero que se
convertirá en BigInt (ver ejemplos).

const str = String();

const num = Number();

const bool = Boolean();

console.log(str); // ->
console.log(num); // -> 0

console.log(bool); // -> false

const big1 = BigInt(42);

console.log(big1); // -> 42n

const big2 = BigInt(); // -> Uncaught TypeError: Cannot convert


undefined to a BigInt

Pero crear valores predeterminados no es nada impresionante. Podemos


lograr esto usando literales. Entonces, ¿para qué usamos estas funciones?
Bueno, los usamos en conversiones de tipos de datos.

Conversiones

Es una situación bastante común tener un valor de un tipo pero necesitar


un valor de otro tipo. El ejemplo más simple es cuando tenemos un
número, pero necesitamos agregarlo a algún texto. Las conversiones en
JavaScript ocurren automáticamente en situaciones específicas, pero
también se pueden usar explícitamente a través de funciones
como String() o Number(). Anteriormente vimos cómo esas funciones
podrían usarse para crear valores predeterminados de esos tipos, pero eso
no es todo lo que pueden hacer. Esas funciones también aceptan
argumentos entre paréntesis y (si es posible) los convertirán a un tipo
dado.

const num = 42;

const strFromNum1 = String(num);


const strFromNum2 = String(8);
const strFromBool = String(true);
const numFromStr = Number("312");
const boolFromNumber = Boolean(0);
La mayoría de estas conversiones son sencillas, pero algunas pueden ser
un poco confusas, así que analicemos cada caso de conversión primitiva.
Prueba todos los ejemplos que se muestran para la conversión de tipo de
dato. Intenta experimentar con tus propios valores.

Conversión a String

Las conversiones son las más fáciles de entender, ya que intentan cambiar
directamente el valor a una cadena, y esto se puede hacer para todos los
tipos de datos primitivos. Así que no hay sorpresas allí. Toma en cuenta
que en el ejemplo, utilizamos la técnica descrita recientemente de
la interpolación de cadenas de caracteres.

let str = "text";


let strStr = String(str);
console.log(`${typeof str} : ${str}`); // -> string : text
console.log(`${typeof strStr} : ${strStr}`); // -> string : text

let nr = 42;
let strNr = String(nr);
console.log(`${typeof nr} : ${nr}`); // -> number : 42
console.log(`${typeof strNr} : ${strNr}`); // -> string : 42

let bl = true;
let strBl = String(bl);
console.log(`${typeof bl} : ${bl}`); // -> boolean : true
console.log(`${typeof strBl} : ${strBl}`); // -> string : true

let bnr = 123n;


let strBnr = String(bnr);
console.log(`${typeof bnr} : ${bnr}`); // -> bigint : 123
console.log(`${typeof strBnr} : ${strBnr}`); // -> string : 123

let un = undefined;
let strUn = String(un);
console.log(`${typeof un} : ${un}`); // -> undefined : undefined
console.log(`${typeof strUn} : ${strUn}`); // -> string : undefined

let n = null;
let strN = String(n);
console.log(`${typeof n} : ${n}`); // -> object : null
console.log(`${typeof strN} : ${strN}`); // -> string : null

Conversión a Number
La conversión a un número no es tan obvia como la conversión a una
cadena. Funciona como se esperaba para cadenas que representan
números reales, como "14", "-72.134", o cadenas que representan números
en notación científica, como "2e3 ", o valores numéricos especiales
como "NaN" o "Infinity".

Sin embargo, la cadena también puede contener números en formato


hexadecimal, octal y binario. Deben estar precedidos por 0x, 0o o 0b
respectivamente. Para cualquier cadena que no se pueda convertir a un
valor especial, se devuelve NaN (no un número). Un BigInt también se
puede convertir en un Number, pero debemos recordar que un BigInt
puede almacenar valores mucho más grandes que un Number, por lo que
para valores grandes, parte de ellos puede ser truncado o terminar siendo
impreciso. El Boolean true se convierte en 1 y false en 0; esto es común
para muchos lenguajes de programación. Un intento de convertir un valor
undefined dará como resultado NaN, mientras que null se convertirá en 0.

console.log(Number(42)); // -> 42

console.log(Number("11")); // -> 11
console.log(Number("0x11")); // -> 17
console.log(Number("0o11")); // -> 9
console.log(Number("0b11")); // -> 3
console.log(Number("12e3")); // -> 12000
console.log(Number("Infinity"));// -> Infinity
console.log(Number("text")); // -> NaN

console.log(Number(14n)); // -> 14
console.log(Number(123456789123456789123n)); // - > 123456789123
456800000

console.log(Number(true)); // -> 1
console.log(Number(false)); // -> 0

console.log(Number(undefined)); // -> NaN

console.log(Number(null));// -> 0

Conversión a Boolean

Las conversiones a Boolean siguen reglas simples, ya que solo podemos


tener uno de dos valores: true o false. El valor false siempre se devuelve
para:

 0,
 NaN,
 cadena vacía,
 undefined,
 null

Cualquier otro valor dará como resultado que se devuelva true.

console.log(Boolean(true)); // -> true

console.log(Boolean(42)); // -> true


console.log(Boolean(0)); // -> false
console.log(Boolean(NaN)); // -> false

console.log(Boolean("texto")); // -> true


console.log(Boolean("")); // -> false

console.log(Boolean(undefined)); // -> false

console.log(Boolean(null)); // -> false

Conversión a BigInt

Para que las conversiones a BigInt tengan éxito, necesitamos un número o


una cadena que represente un número como un valor a convertir. Los
valores para la conversión se pueden proporcionar en forma decimal
predeterminada, así como en forma hexadecimal, octal o binaria. Esto se
aplica tanto a la situación en la que damos los literales Number y String
como argumentos (o variables que contienen datos de esos tipos). También
podemos usar la notación exponencial, pero solo para argumentos
Number. A diferencia de otras conversiones, la conversión a BigInt
generará un error y detendrá el programa cuando no pueda convertir un
valor dado.

Nota: Al probar el siguiente ejemplo, presta atención al hecho de que el


primer error impide la ejecución de más código. Así que ejecuta el ejemplo
varias veces seguidas, eliminando las llamadas incorrectas una por una.

console.log(BigInt(11)); // -> 11n


console.log(BigInt(0x11)); // -> 17n
console.log(BigInt(11e2)); // -> 1100n

console.log(BigInt(true)); // -> 1n

console.log(BigInt("11")); // -> 11n


console.log(BigInt("0x11")); // -> 17n

console.log(BigInt(null)); // -> Uncaught TypeError: Cannot convert null to


a BigInt
console.log(BigInt(undefined)); // -> Uncaught TypeError: Cannot convert
undefined to a BigInt

console.log(BigInt(NaN)); // -> Uncaught RangeError: The number NaN


cannot be converted to a BigInt because it is not an integer

Conversiones Implícitas

Las conversiones también pueden ocurrir automáticamente y ocurren todo


el tiempo. Este ejemplo simple lo demostrará (probamos un ejemplo similar
cuando discutimos el tipo de dato String):

const str1 = 42 + "1";


console.log(str1); // -> 421
console.log(typeof str1); // -> string

const str2 = 42 - "1";


console.log(str2); // -> 41
console.log(typeof str2); // -> number

Entonces, ¿qué está pasando? Los detalles se mostrarán en el capítulo


sobre operadores, pero la respuesta corta es que cuando intentamos
realizar una suma cuando uno de los argumentos es una cadena,
JavaScript también convertirá el resto de los argumentos en una cadena.
Esto es lo que sucede con str1 en el ejemplo. Sin embargo, la resta con
una cadena no tiene mucho sentido, por lo que, en ese caso, JavaScript
convierte todo a Number.

Tareas

Tarea 1

Escribe un código que cree variables y las inicialice con


valores Boolean, Number, BigInt, String y tipos undefined usando (cuando
sea posible) literales y funciones constructoras.

Solución
let b1 = true;

let b2 = Boolean(true);

let n1 = 100;

let n2 = Number(200);

let bi1 = 100n;

let bi2 = BigInt(200);

let s1 = "Hello";

let s2 = String("Hello");

let u1 = undefined;

Tarea 2

Imprime todos los valores y todos los tipos de esos valores


usando console.log. Intenta usar la interpolación de cadenas para mostrar
el valor y el tipo al mismo tiempo con una sola llamada a console.log.

Solución

console.log(`${b1} [${typeof b1}]`);

console.log(`${b2} [${typeof b2}]`);

console.log(`${n1} [${typeof n1}]`);

console.log(`${n2} [${typeof n2}]`);


console.log(`${bi1} [${typeof bi1}]`);

console.log(`${bi2} [${typeof bi2}]`);

console.log(`${s1} [${typeof s1}]`);

console.log(`${s2} [${typeof s2}]`);

console.log(`${u1} [${typeof u1}]`);

Tarea 3

Realizar una cadena de conversiones: crear un Boolean a partir de


un BigInt creado a partir de un Number que se creó a partir de un String .
Comienza con el valor "1234". ¿Es posible?

Ejemplo

let b = Boolean( BigInt(Number("1234")));

console.log(`${b} [${typeof b}]`);

// or

let s = "1234";

let n = Number(s);

let bi = BigInt(n);

let b = Boolean(bi);

console.log(`${b} [${typeof b}]`);


Tarea 4

Intenta agregar dos valores del mismo tipo y verifica el tipo de resultado.
Pruébalo para todos los tipos primitivos.

Ejemplo

let b = true + false;

let n = 100 + 200;

let bi = 100n + 200n;

let s = "He" + "llo";

let u = undefined + undefined;

console.log(`${b} [${typeof b}]`); // !!! number

console.log(`${n} [${typeof n}]`);

console.log(`${bi} [${typeof bi}]`);

console.log(`${s} [${typeof s}]`);

console.log(`${u} [${typeof u}]`); // !!! number

Tarea 5

Prueba sumar dos valores de diferentes tipos y verifica los resultados.

Ejemplos

let b1 = true + 100;

// let b2 = true + 100n; // -> error!

let b3 = true + "100";

// let n1 = 100 + 200n; // -> error!


let n2 = 100 + true;

let n3 = 100 + "200";

// let bi1 = 100n + 200; // -> error!

// let bi2 = 100n + true // -> error!

let bi3 = 100n + "200";

let s1 = "100" + 200;

let s2 = "100" + 200n;

let s3 = "100" + true;

let s4 = "abc" + 200;

let s5 = "abc" + 200n;

let s6 = "abc" + true;

console.log(`${b1} [${typeof b1}]`); // -> 101 [number]

// console.log(`${b2} [${typeof b2}]`);

console.log(`${b3} [${typeof b3}]`); // -> true100 [string]

// console.log(`${n1} [${typeof n1}]`);

console.log(`${n2} [${typeof n2}]`); // -> 101 [number]

console.log(`${n3} [${typeof n3}]`); // -> 100200 [string]

// console.log(`${bi1} [${typeof bi1}]`);

// console.log(`${bi2} [${typeof bi2}]`);

console.log(`${bi3} [${typeof bi3}]`); // -> 100200 [string]

console.log(`${s1} [${typeof s1}]`); // -> 100200 [string]

console.log(`${s2} [${typeof s2}]`); // -> 100200 [string]

console.log(`${s3} [${typeof s3}]`); // -> 100true [string]


console.log(`${s4} [${typeof s4}]`); // -> abc200 [string]

console.log(`${s5} [${typeof s5}]`); // -> abc200 [string]

console.log(`${s6} [${typeof s6}]`); // -> abctrue [string]

Tarea 6

Intenta modificar la línea const str1 = 42 + "1"; para obtener el


resultado 43 (sin eliminar las comillas alrededor del 1 ).

Ejemplo

const str1 = 42 + +"1";

Tipos de datos complejos

Limitaremos la discusión de tipos complejos a solo dos de ellos: objetos y


arreglos. Desafortunadamente, incluso estos tipos deberán presentarse de
manera simplificada. Esto debería ser suficiente para usarlos en su ámbito
básico, pero las técnicas más avanzadas relacionadas con ellos, así como
otros tipos complejos, se presentarán en las siguientes partes del curso.

Objeto

Los objetos tienen muchas aplicaciones en JavaScript. Una de las más


básicas, y la única que usaremos ahora, es utilizarlo como una estructura
conocida en informática como registro. Un registro es una colección de
campos con nombre. Cada campo tiene su propio nombre (o clave) y un
valor asignado. En el caso de los objetos de JavaScript, estos campos
suelen denominarse propiedades. Los registros, o en nuestro caso, los
objetos, te permiten almacenar múltiples valores de diferentes tipos en un
solo lugar. En JavaScript, hay algunas formas de crear objetos, pero la
más fácil y rápida es usar el literal de llaves {}.

let testObj = {};

console.log(typeof testObj); // -> object


El objeto que creamos y almacenamos en la variable testObj no es
particularmente útil porque está... vacío. No hemos definido ningún campo
en él, es decir, ningún par clave-valor. Intentémoslo de nuevo, esta vez
definiendo un objeto que contenga dos campos con las claves nr y str.

let testObj = {

nr: 600,

str: "texto"

};

¿Para qué necesitamos objetos? La razón más simple para usarlos puede
ser el deseo de almacenar varios valores en un solo lugar, que están
vinculados entre sí por alguna razón.

Supongamos que recopilamos información sobre los usuarios de nuestro


sistema. La información sobre un solo usuario consistirá en su nombre,
apellido, edad y dirección de correo electrónico. Intentemos escribir un
fragmento de código apropiado para dos usuarios, sin usar objetos por
ahora.

let name1 = "Calvin";


let surname1 = "Hart";
let age1 = 66;
let email1 = "[email protected]";

let name2 = "Mateus";


let surname2 = "Pinto";
let age2 = 21;
let email2 = "[email protected]";

Parece que todo funciona correctamente, pero si lo pensamos bien,


notaremos dos inconvenientes. En primer lugar, para cada usuario,
deberás crear nombres de variables separados para el apellido, el correo
electrónico, etc. ¿Qué sucede si describimos a cada usuario con un poco
más de precisión? ¿O si no fueran solo dos usuarios, sino, digamos, mil?
Entonces sería menos inconveniente. Hasta cierto punto, podemos
arreglarlo con objetos. El segundo problema es que ya en la etapa de
escritura, necesitamos saber el número exacto de usuarios que se
describirán en el sistema. Esto sería extremadamente limitante en
aplicaciones reales y sería mejor poder agregarlas dinámicamente.
También podremos mejorar esto, no con objetos, sino con arreglos (más
sobre esto en un momento).

Así que mejoremos nuestro de código con objetos:

let user1 = {
name: "Calvin",
surname: "Hart",
age: 66,
email: "[email protected]"
};

let user2 = {
name: "Mateus",
surname: "Pinto",
age: 21,
email: "[email protected]"
};

Todavía tenemos que dar nombres diferentes a las variables que


almacenan información (en forma de objetos) sobre usuarios individuales,
pero esta vez las propiedades pueden tener los mismos nombres. Esto
hace que el código no solo sea más claro y coherente, sino que también
facilita la realización de acciones en las propiedades de diferentes
usuarios.

Las propiedades de un objeto, como hemos indicado anteriormente, se


ponen a disposición con un punto y un nombre clave. Podemos tanto leer
como modificar el valor asociado a una clave en particular. Además,
también podemos modificar todo el objeto añadiendo una nueva propiedad
que antes no existía. También hacemos esto usando la notación de puntos:
si durante un intento de modificar la propiedad, el intérprete no encuentra
la clave que especificamos, la creará.

console.log(user1.name); // -> Calvin


console.log(user2.name); // -> Mateus

console.log(user1.age); // -> 66
user1.age = 67;
console.log(user1.age); // -> 67

console.log(user2.phone); // -> undefined


user2.phone = "904-399-7557";
console.log(user2.phone); // -> 904-399-7557

Si puedes agregar nuevos campos a un objeto existente, ¿también puedes


eliminarlos? Por supuesto que puedes: el operador de eliminación se usa
para esto.

console.log(user2.phone); // -> 904-399-7557


delete user2.phone;
console.log(user2.phone); // -> undefined

La usabilidad de los objetos va mucho más allá de usarlos como


estructuras de almacenamiento de datos. Sin embargo, es un tema aparte,
en gran medida relacionado con la programación orientada a objetos, que
forma parte de técnicas de programación más avanzadas. En nuestro caso,
los objetos serán estructuras simples, formadas por pares de clave-valor.

Arreglos

Un arreglo, como un objeto, es un tipo de datos complejo que se puede


usar para almacenar una colección de datos. Similar a un objeto, los datos
almacenados (los valores) pueden ser de cualquier tipo. La diferencia entre
estas estructuras es que en un arreglo solo almacenamos valores, sin los
nombres asociados (es decir, las claves).

Entonces, ¿cómo sabemos a qué elemento del arreglo nos referimos si no


podemos señalarlo por su nombre? Lo sabemos porque los elementos del
arreglo están ordenados (pero no necesariamente clasificados) y ocupan
posiciones consecutivas y numeradas dentro de el. El número del campo
donde se encuentra un valor particular en el arreglo se denomina índice o
posición. El índice comienza desde 0.

La forma más sencilla de crear arreglos en JavaScript es usar corchetes (es


un literal de arreglo). De esta forma, podemos crear tanto un arreglo vacío,
en el que se insertarán los elementos más tarde, como un arreglo que
contenga algunos elementos iniciales (que estarán separados por comas).
Al referirnos a un elemento del arreglo en particular, usamos la notación
de corchetes: después del nombre de la variable del arreglo, escribimos un
corchete, en el que ponemos el índice del elemento que nos interesa.
Veamos un ejemplo sencillo:

let days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];


console.log(days[0]); // -> Sun
console.log(days[2]); // -> Tue
console.log(days[5]); // -> Fri

days[0] = "Sunday";
console.log(days[0]); // -> Sunday

let emptyArray = [];


console.log(emptyArray[0]); // -> undefined

Para comenzar, hemos declarado e iniciado el arreglo days, que contiene


siete nombres abreviados de los días de la semana. Los elementos de este
arreglo son datos del tipo String. Teniendo en cuenta que los índices (las
posiciones de los elementos) en el arreglo comienzan desde 0, mostramos
tres días de la semana seleccionados en la consola. Luego cambiamos el
elemento en el índice 0, y el valor "Sun" se reemplaza por "Sunday". El
último fragmento de código es una declaración de un arreglo vacío y un
intento de leer un elemento inexistente en el arreglo.

¿Cómo podemos agregar un nuevo elemento a un arreglo existente, por


ejemplo, uno vacío?

La forma más sencilla sería asignar un nuevo valor a una posición


específica utilizando la notación de corchetes. Para el intérprete, no
importa si ya hay algo en este índice o no. Simplemente coloca un nuevo
valor allí. Lo interesante es que no tenemos que llenar el arreglo con
elementos uno por uno; puedes dejar espacios vacíos en el arreglo.

let animals = [];


console.log(animals[0]); // -> undefined

animals[0] = "dog";
animals[2] = "cat";

console.log(animals[0]); // -> dog


console.log(animals[1]); // -> undefined
console.log(animals[2]); // -> cat
En el ejemplo, declaramos un arreglo vacío llamado animals. Luego
colocamos dos elementos, "dog" y "cat", en las posiciones 0 y 2, dejando la
posición 1 vacía. Sin embargo, esta no es la única forma de agregar nuevos
elementos en el arreglo, y presentaremos otros en un momento, así como
formas de eliminarlos.

Por lo general, almacenamos el mismo tipo de dato en un arreglo, pero


como mencionamos anteriormente, JavaScript no lo requiere. Entonces
podemos crear fácilmente un arreglo que contenga elementos de diferentes
tipos.

let values = ["Test", 7, 12.3, false];

Como ya hemos dicho, un elemento del arreglo puede ser de cualquier


tipo. Lo que es interesante es el hecho de que también podemos almacenar
arreglos como elementos de un arreglo, y podemos acceder a los elementos
de este arreglo anidado usando múltiples corchetes.

let names = [["Olivia", "Emma", "Mia", "Sofia"], ["William", "James",


"Daniel"]];
console.log(names[0]); // -> ["Olivia", "Emma", "Mia", "Sofia"]
console.log(names[0][1]); // -> Emma
console.log(names[1][1]); // -> James

let femaleNames = names[0];


console.log(femaleNames[0]); // -> Olivia
console.log(femaleNames[2]); // -> Mia

El ejemplo muestra una declaración de un arreglo que contiene otros dos


arreglos como sus componentes. Toma en cuenta que los arreglos internos
no tienen que tener la misma longitud (en muchos otros lenguajes de
programación, esto es obligatorio).

¿Para qué pueden ser útiles los arreglos en la práctica?

Son principalmente una forma conveniente de almacenar una colección de


elementos bajo un nombre. Además, es muy importante que podamos
agregar nuevos elementos a un arreglo mientras el programa se está
ejecutando.

¿Recuerdas el ejemplo con los usuarios del sistema que probamos


mientras discutíamos los objetos? Una de las desventajas de la solución
presentada fue la necesidad de declarar variables para todos los usuarios,
por lo que en la etapa de escritura del programa teníamos que saber el
número de usuarios. Usando un arreglo, podemos agregar nuevos
usuarios mientras se ejecuta el programa. Mencionamos varias veces que
los elementos de un arreglo pueden ser cualquier dato, incluidos los
objetos. Como recordatorio, repitamos el ejemplo en el que declaramos dos
variables del tipo objeto, user1 y user2, que contienen información sobre
dos usuarios del sistema:

let user1 = {
name: "Calvin",
surname: "Hart",
age: 66,
email: "[email protected]"
};

let user2 = {
name: "Mateus",
surname: "Pinto",
age: 21,
email: "[email protected]"
};

Pongamos información sobre estos dos usuarios en el arreglo de usuarios e


intentemos mostrar alguna información como parte de la prueba:

let users =[
{
name: "Calvin",
surname: "Hart",
age: 66,
email: "[email protected]"
},
{
name: "Mateus",
surname: "Pinto",
age: 21,
email: "[email protected]"
}
];

console.log(users[0].name); // -> Calvin


console.log(users[1].age); // -> 21
Intentemos agregar un nuevo usuario en el arreglo. Lo haremos de la
única forma que conocemos hasta ahora, que es asignando un nuevo
elemento a un índice libre (esto es una continuación del ejemplo anterior).

users[2] = {
name: "Irene",
surname: "Purnell",
age: 32,
email: "[email protected]"

console.log(users[0].name); // -> Calvin


console.log(users[1].name); // -> Mateus
console.log(users[2].name); // -> Irene

Durante la operación del programa, es posible interactuar con el usuario,


por ejemplo, para recuperar el arreglo con elementos que no conocíamos
mientras escribíamos el programa.

Ahora hagamos un pequeño experimento y apliquemos el operador typeof a


la variable que contiene el arreglo. El resultado puede ser algo
sorprendente:

let days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];


console.log(typeof days); // -> object

Para hablar en general, en JavaScript, todo excepto los datos primitivos


son objetos. Los arreglos también se tratan como un tipo especial de
objeto. El operador typeof no distingue entre tipos de objetos (o más
precisamente, clases), por lo que nos informa que la variable days contiene
un objeto. Si queremos asegurarnos de que la variable contiene un arreglo,
podemos hacerlo usando el operador instanceof, entre otros. Está
estrechamente relacionado con la programación orientada a objetos, de la
que no hablaremos en este curso, pero por el momento solo necesitamos
saber cómo usarlo en esta situación específica.
let days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
let day = "Sunday";

console.log(typeof days); // -> object


console.log(typeof day); // -> string

console.log(days instanceof Array); // -> true


console.log(day instanceof Array); // -> false

El operador instanceof es un operador de dos argumentos, que requiere


que se especifique la variable probada (o literal) y la clase de objeto. En
nuestro caso, la clase Array. El operador devuelve verdadero (true) o falso
(false), según el resultado de la prueba.

Recientemente presentamos los conceptos de método y propiedad.


Aparecieron cuando hablábamos del tipo String. Estas eran funciones y
valores relacionados con un objeto específico. Ahora resulta que un arreglo
se implementa como un objeto en JavaScript, por lo que probablemente
también tenga sus métodos y propiedades. Y de hecho los tiene. Hay
muchos métodos muy útiles que nos ayudan a trabajar con arreglos, como
combinar arreglos, cortar elementos, ordenar o filtrar.

Solo veremos algunos de ellos ahora, porque muchos otros requieren que
podamos crear nuestras propias funciones. Volveremos sobre algunos de
ellos en el apartado dedicado a las funciones.

length

La propiedad length se utiliza para obtener información sobre la longitud


(la cantidad de elementos) del arreglo (incluidas las posiciones vacías entre
los elementos existentes).

let names = ["Olivia", "Emma", "Mateo", "Samuel"];

console.log(names.length); // -> 4

names[5] = "Amelia";

console.log(names.length); // -> 6
console.log(names); // -> ["Olivia", "Emma", "Mateo", "Samuel", undefined,
"Amelia"]

console.log(names[3]); // -> Samuel

console.log(names[4]); // -> undefined

console.log(names[5]); // -> Amelia

indexOf

El método indexOf se utiliza para buscar en el arreglo y localizar un valor


dado. Si se encuentra el valor (el elemento está en el arreglo), se devolverá
su índice (posición). El método devuelve -1 si no se encuentra el elemento.
Si hay más de un elemento con el mismo valor en el arreglo, se devuelve el
índice del primer elemento encontrado.

let names = ["Olivia", "Emma", "Mateo", "Samuel"];


console.log(names.indexOf("Mateo")); // -> 2
console.log(names.indexOf("Victor")); // -> -1

Toma en cuenta que hemos creado objetos usando el mismo literal, pero al
mismo tiempo hemos creado propiedades que son pares clave-valor. Las
propiedades están separadas por comas. Posteriormente, se puede hacer
referencia a una propiedad (campo) específica de un objeto con notación de
puntos. Esta notación requiere que el nombre del objeto (un literal o el
nombre de una variable que contiene el objeto) sea seguido por un punto,
seguido por el nombre del campo (clave) nuevamente.

console.log(testObj.nr); // -> 600

console.log(testObj.str); // -> texto

push

El método push coloca el elemento dado como su argumento al final del


arreglo. La longitud del arreglo aumenta en 1 y el nuevo elemento se
inserta a la derecha (tiene el índice más grande de todos los elementos).

let names = ["Olivia", "Emma", "Mateo", "Samuel"];


console.log(names.length); // -> 4

names.push("Amelia");
console.log(names.length); // -> 5
console.log(names); // - > ["Olivia", "Emma", "Mateo","Samuel", "Amelia"]

unshift

El método unshift funciona de manera similar a push, con la diferencia de


que se agrega un nuevo elemento al inicio del arreglo. La longitud de
arreglo aumenta en 1, todos los elementos antiguos se mueven hacia la
derecha y el nuevo elemento se coloca en el espacio vacío que se ha creado
al inicio del arreglo. El índice del nuevo elemento es 0.

let names = ["Olivia", "Emma", "Mateo", "Samuel"];


console.log(names);
console.log(names.indexOf("Mateo")); // -> 2
console.log(names.indexOf("Victor")); // -> -1 -> No existe
names.unshift("Amelia");
console.log(names.indexOf("Amelia")); // -> 0
console.log(names);

pop

El método pop te permite eliminar el último elemento del arreglo. Como


resultado de su ejecución, se devuelve el elemento con mayor índice,
mientras que al mismo tiempo se elimina del arreglo original. La longitud
del arreglo obviamente se reduce en 1.

let names= ["Olivia", "Emma", "Mateo", "Samuel"];


console.log(names.length); // -> 4

let name = names.pop();


console.log(names.length); // -> 3
console.log(name); // -> Samuel
console.log(names); // -> ["Olivia", "Emma", "Mateo"]
shift

El método shift funciona de manera similar a pop, solo que esta vez
eliminamos el elemento del inicio del arreglo (con el índice 0). El método
devuelve el elemento eliminado y todos los demás elementos se desplazan
hacia la izquierda, llenando el espacio vacío. La longitud del arreglo
original se reduce en 1.

let names = ["Olivia", "Emma", "Mateo", "Samuel"];


console.log(names.length); // -> 4

let name = names.shift();


console.log(names.length); // -> 3
console.log(name); // -> Olivia
console.log(names); // -> ["Emma", "Mateo", "Samuel"]
reverse

El método reverse invierte el orden de los elementos de un arreglo. El


método devuelve un arreglo con los elementos en orden inverso.

let names = ["Olivia", "Emma", "Mateo", "Samuel"];

names.reverse();
console.log(names); // -> ["Samuel", "Mateo", "Emma", "Olivia"]

slice

El método slice te permite crear un nuevo arreglo a partir de elementos


seleccionados del arreglo original. Llamar al método no afecta el arreglo
original. El método toma uno o dos valores enteros como argumentos.

Las combinaciones básicas son:

 Un argumento mayor que cero: se copian todos los elementos del


índice dado como argumento hasta el final del arreglo.
 Dos argumentos mayores que cero: se copia el elemento del índice
especificado como primer argumento al elemento especificado como
segundo argumento.
 Dos argumentos, el primero positivo, el segundo negativo: se copian
todos los elementos desde el índice especificado hasta el final del
arreglo, excepto el número especificado de los últimos elementos (por
ejemplo, el argumento -3 significa que no copiamos los últimos tres
elementos).
 Un argumento negativo: el número especificado de los últimos
elementos se copian al final del arreglo (por ejemplo, -2 significa que
se copian los dos últimos elementos).
let names = ["Olivia", "Emma", "Mateo", "Samuel"];
let n1 = names.slice(2);
console.log(n1); // -> ["Mateo", "Samuel"]

let n2 = names.slice(1,3);
console.log(n2); // -> ["Emma", "Mateo"]

let n3 = names.slice(0, -1);


console.log(n3); // -> ["Olivia", "Emma", "Mateo"]

let n4 = names.slice(-1);
console.log(n4); // -> ["Samuel"]

console.log(names); // -> ["Olivia", "Emma", "Mateo","Samuel"]

concat

El método concat crea un nuevo arreglo adjuntando elementos del arreglo


dado como argumento a los elementos originales del arreglo. El método no
cambia ni el arreglo original ni el arreglo especificado como argumento.

let names = ["Olivia", "Emma", "Mateo", "Samuel"];


let otherNames = ["William", "Jane"];
let allNames = names.concat( otherNames);

console.log(names); // -> ["Olivia", "Emma", "Mateo", "Samuel"]


console.log(otherNames); // -> ["William", "Jane"]
console.log(allNames); // -> ["Olivia", "Emma", "Mateo","Samuel",
"William", "Jane"]

Resumen

Este capítulo contiene bastante información. Comenzamos el capítulo con


una discusión sobre tipos de datos simples, que aparte del tipo String, en
realidad no debería causar ningún problema. Los tipos Number, BigInt o
Boolean no se llaman por casualidad primitivos.

Mientras discutíamos el tipo String, aprendimos lo qué


es autoboxing (conversión automática de un primitivo a un objeto
relacionado con ese primitivo) y cómo podemos usar métodos relacionados
con el objeto String.

Algún espacio se dedicó a discutir la conversión de tipos de datos. Al


final vimos información básica sobre tipos complejos, limitándonos a
objetos y arreglos. Recuerda que introdujimos los objetos en una forma
muy simplificada, lo que nos permitirá usarlos como registros (es decir,
tipos de datos que consisten en campos clave-valor). Lo que discutimos
está relacionado con la programación orientada a objetos, de la que puedes
haber oído hablar, pero que no forma parte del curso actual.

Los arreglos se han discutido con un poco más de detalle. Volveremos


sobre ellos más de una vez, porque son uno de los elementos básicos
utilizados en la práctica. Los ampliaremos en la parte de bucles y
funciones del curso.

Hasta ahora, los arreglos son el elemento de programación más


complicado que hemos aprendido. Así que no sería de extrañar que
tuvieras que pasar por esta parte del curso una vez más. Los arreglos son
muy importantes, por lo que vale la pena tomarse un poco más de tiempo
para entenderlos bien. Volveremos a los arreglos más de una vez, entre
otras cosas cuando hablemos de bucles y funciones.

Tareas

Objetos

Tarea 1

Crea un objeto que describa un boleto de tren y guárdalo en la variable


ticket. El objeto debe tener tres campos:

 estación inicial (el nombre clave es from, y como valor, proporciona


el nombre de la estación más cercana en tu área)
 estación final (el nombre clave es to, y como valor, dar cualquier otra
estación dentro de 100 km)
 el precio del boleto (el nombre clave es price, y como valor,
proporciona la cantidad que te gustaría pagar por este boleto)
El objeto debe crearse usando llaves {}, en los que todos los campos
se enumerarán inmediatamente. Luego muestra los valores de todos
los campos del ticket en la consola.

Ejemplo

let ticket = {

from: "Berlin",

to: "Potsdam",

price: 11

};

console.log(`Ticket from: ${ticket.from}`);

console.log(`Ticket to: ${ticket.to}`);

console.log(`Ticket price: ${ticket.price}`);

Task 2

Declara un objeto vacío y guárdalo en la variable person. Usando la


notación de punto, agrega los campos name y surname al objeto
ingresando tus datos como valores. Intenta mostrar los campos
individuales en la consola.

Ejemplo

let person = {};

person.name = "Mary";

person.surname = "Stuart";

console.log(`${person.name} ${person.surname}`);

Arreglos
Tarea 3

Estamos creando una pequeña biblioteca de libros sobre


programación en JavaScript. Tenemos tres libros y queremos
preparar una lista de ellos. Almacenaremos tres datos de cada libro:
el título, el autor y el número de páginas:

o Speaking JavaScript, Axel Rauschmayer, 460;


o Programming JavaScript Applications, Eric Elliott, 254;
o Understanding ECMAScript 6, Nicholas C. Zakas, 352.
 Crea un arreglo de tres objetos que representen los libros. Cada
objeto debe tener las siguientes propiedades: title, author, pages

Ejemplo

let books = [{

title: "Speaking JavaScript",

author: "Axel Rauschmayer",

pages: 460

},

title: "Programming JavaScript Applications",

author: "Eric Elliot",

pages: 254

},

title: "Understanding ECMAScript 6",

author: "Nicholas C. Zakas",

pages: 352

}
];

Tarea 4

Agregar un nuevo libro a la colección: Learning JavaScript Design


Patterns, por Addy Osmani, 254 páginas. Usa el método apropiado
para adjunta el libro al final del arreglo. Muestra la longitud del
arreglo y, a su vez, todos los nombres de los libros en la colección.

Ejemplo

let newBook = {

title: "Learning JavaScript Design Patterns",

author: "Addy Osmani",

pages: 254

};

books.push(newBook);

console.log(books.length);

console.log(books[0].title);

console.log(books[1].title);

console.log(books[2].title);

console.log(books[3].title);

Task 5

Utiliza el comando slice para copiar los dos últimos libros al nuevo
arreglo.

Ejemplo
let selectedBooks = books.slice(-2);

Tarea 6

El primer libro de la colección se pierde en circunstancias


inexplicables. Ya has aceptado la pérdida, así que ahora elimínalo
del arreglo. ¿Cuál método usarás para este propósito? Muestra la
longitud del arreglo y todos los nombres de los libros de la colección
a su vez.

Ejemplo

books.shift();

console.log(books.length);

console.log(books[0].title);

console.log(books[1].title);

console.log(books[2].title);

Tarea 7

Muestra la suma de las páginas de todos los libros de la colección.

Ejemplo

let sum = books[0].pages + books[1].pages + books[2].pages;

console.log(`pages: ${sum}`);

LABORATORIO
Tiempo estimado

15-30 minutos

Nivel de dificultad

Fácil

Objetivos

Familiarizar al estudiante con:

 Las propiedades básicas de los tipos de datos complejos Array y


Object (usados como un registro) y ser capaz de usarlos en la
práctica.

Escenario

¿Recuerdas la lista de contactos que se creó mientras realizabas la tarea


del laboratorio anterior? Tienes que admitir que a primera vista parecía
bastante extraño. Tuvimos que usar nueve variables para almacenar
información sobre solo tres usuarios. Lo que es peor, agregar cada nuevo
usuario requeriría la creación de tres variables más. Esto no es
conveniente ni práctico. Afortunadamente, desde entonces hemos
aprendido algo sobre arreglos y objetos, lo que nos permitirá guardar
nuestra lista de una manera un poco más conveniente. Con los mismos
datos que en el laboratorio anterior, crea la lista de contactos como un
arreglo, cada elemento del cual será un objeto que describa a un solo
usuario. Entonces, deberíamos tener un arreglo de tres elementos, y cada
objeto colocado en el contendrá tres piezas de información (nombre,
teléfono y correo electrónico).

Al final de la lista declarada de esta manera, agrega un nuevo contacto


usando el método del arreglo apropiado. El nuevo contacto es: Maisie
Haley / 0913 531 3030 / [email protected].

Muestra el primer y último contacto, de nuevo en el formato: nombre /


teléfono / correo electrónico. Utiliza la propiedad length del arreglo para
determinar el índice del último elemento. Recuerde que los elementos del
arreglo se indexan a partir de 0.

let contacts = [{

name: "Maxwell Wright",

phone: "(0191) 719 6495",


email: "[email protected]"

}, {

name: "Raja Villarreal",

phone: "0866 398 2895",

email: "[email protected]"

}, {

name: "Helen Richards",

phone: "0800 1111",

email: "[email protected]"

}];

let newcontact={name: " Maisie Haley",

phone: "0913 531 3030",

email: "[email protected]"};

contacts.push(newcontact);

console.log(contacts.length);

console.log(contacts[0].name);

console.log(contacts[0].phone);

console.log(contacts[0].email);

console.log(contacts[3].name);

console.log(contacts[3].phone);

console.log(contacts[3].email);

Comentarios

Los comentarios son algo común en la programación. "Comentar" puede no


ser una técnica de programación clave (si se le puede llamar técnica), pero
te permite mejorar tu trabajo con el código, entre otras cosas, haciéndolo
más legible. Entonces, ¿qué son los comentarios y por qué los
necesitamos?

Los comentarios son solo texto sin formato, totalmente ignorados por el
intérprete de JavaScript, que generalmente sirven para explicar una
determinada pieza de código, que por algunas razones puede no ser
completamente legible. Sin embargo, no podemos escribirlos con total
libertad, ya que el intérprete intentará tratarlos como comandos, nombres
de variables o palabras clave. Entonces JavaScript necesita distinguir los
comentarios del resto del código. En JavaScript, tenemos dos tipos de
comentarios, y ambos se usan comúnmente en muchos lenguajes de
programación, desde la familia de lenguajes C hasta Python. Se
denominan comentarios de una sola línea y de varias líneas.

Comentarios de una sola línea

Esta es la forma principal de comentar el código. Utiliza un carácter de


doble diagonal al comienzo del comentario que se extiende hasta el final de
la línea. El intérprete ignorará cualquier código colocado a la derecha de la
doble diagonal. Si queremos crear comentarios de esta forma en varias
líneas, tenemos que poner // en cada línea por separado. Probablemente
hayas notado que hemos usado este tipo en ejemplos anteriores.

// Este es un comentario de una sola línea

let x = 42; // Este también es un comentario de una sola línea, aunque la


parte anterior a la doble diagonal es un código que se ejecutará.

// Esta línea y la siguiente serán ignoradas

// x = 8;

console.log(x); // -> 42

En la mayoría de los editores de código modernos, es posible "comentar"


fragmentos de código usando métodos abreviados del teclado. Esto
significa que podemos colocar el cursor en la línea de código seleccionada
y presionando una combinación específica de teclas podemos colocar el
signo de comentario al inicio de la línea. Presionar la misma combinación
nuevamente "descomentará" el carácter del comentario. Por lo general,
también es posible marcar varias líneas (con un mouse o usando las teclas
de flecha con la tecla Shift presionada) y colocar un carácter de comentario
al comienzo de todas las líneas marcadas usando la misma combinación
de teclas. Este mecanismo es compatible con aplicaciones como Sublime
Text o Visual Studio Code, dos editores que recomendamos anteriormente.
Pruébalo en el editor de la plataforma OpenEDG que estás utilizando
actualmente. Esta combinación de teclas de la que hablamos es la más
habitual en Windows:

mientras que en macOS:

Este método se usa con mayor frecuencia para "comentar" (es decir,
deshabilitar temporalmente) un fragmento de código seleccionado, por
ejemplo, para probar una versión alternativa del mismo.

Comentarios de varias líneas

Los comentarios de varias líneas, también conocidos como comentarios de


bloque, permiten que los comentarios abarquen varias líneas, pero
también te permiten colocar comentarios dentro de una línea, lo que no es
posible con los comentarios de una sola línea. Se crean con una diagonal y
un asterisco al principio del comentario y un asterisco y una diagonal al
final.

/*
Este es un bloque
de comentarios y puede
abarcar varias líneas

Entonces este código no se ejecutará


console.log("¡Hola, Mundo!");
*/

let x /* porque no hay mejor nombre */ = 42;


console.log(x);

Pero, ¿por qué comentar en primer lugar?


Como ya se dijo, es muy importante dar nombres apropiados e
informativos a las variables (y también a las funciones y cualquier otra
cosa que podamos nombrar), pero incluso los mejores nombres no
ayudarán si el código es complejo, y el código se vuelve complejo con
bastante facilidad. Entonces, para ayudar a los desarrolladores a expresar
su intención para una pieza de código dada, usan comentarios para
explicarse más verbalmente.

Por supuesto, estos son ejemplos exagerados de comentarios solo para


mostrarte cómo usarlos. En las aplicaciones y secuencias de comandos de
la vida real, los comentarios deben usarse con prudencia, ya que
demasiados comentarios obvios harán que el código sea aún más difícil de
leer.

Como regla general, los comentarios deben usarse cuando la lectura del
código no es suficiente para comprender lo que hace, o en situaciones en
las que el código se comporta de manera diferente a lo esperado y necesita
demostrar que es intencional. También debes recordar que en la mayoría
de los proyectos comerciales, no eres la única persona que leerá este
código. E incluso si lo fueras, leer tu propio código después de unos meses
es un desafío, y leer el código de otra persona puede llevar ese desafío al
siguiente nivel.

// inicializar greetingText con Hola

const greetingText = "Hola";

angle = angle + 90; // girar 90 grados para compensar la pantalla vertical

// comprobar que 0 no sea el divisor

let result = a / b;

// no hay necesidad de comprobar el divisor

let result = a / b;

// dividir a entre b

let result = a / b;
Documentación

Podemos usar comentarios para documentar el código y escribir


exactamente qué funciones hace y qué parámetros requiere. En muchos
proyectos, los archivos tienen un encabezado con información sobre el
autor, la licencia o el historial de cambios. Hay herramientas que pueden
generar documentación automáticamente a partir de comentarios, siempre
que se utilicen de acuerdo con la referencia de las herramientas. Un
ejemplo de tal herramienta podría ser JSDoc. Colocar comentarios en el
código de acuerdo con el formato de esta herramienta permitirá generar un
sitio web que contenga información detallada sobre un proyecto (por
ejemplo, descripciones de funciones, sus parámetros de llamada, valores
devueltos, etc.).

Alternar código

A veces tenemos algún fragmento de código que creemos que está


causando algunos problemas o queremos comprobar algunas opciones
rápidamente. Comentar el código es una gran herramienta para ayudar
con eso.

// const greetingText = "Hola";

const greetingText = "Hola";

// const greetingText = "Bienvenido";

Podemos deshabilitar cada línea de código usando comentarios de una


sola línea como en el ejemplo. Usando comentarios de bloque, podemos
comentar grandes fragmentos de código o funciones completas. Esto es
muy útil cuando se identifican algunos problemas de código.

Tarea

Hay un código que actualmente no funciona. Intenta arreglarlo usando


solo comentarios. Intenta, si es posible, usar los atajos de teclado en tu
editor para este propósito.

Revisar

"use strict";
const prefix = "username_";

let userName = "Jack";

// const userName = "Adam";

let prefixedUserName;

// const prefixedUserName;

userName = "John";

prefixedUserName = prefix + userName;

console.log(prefixedUserName /*+ prefixedUserName2*/);

// console.log(prefixedUserName2);

Operadores

Los operadores en los lenguajes de programación son símbolos (a veces


también nombres) que se utilizan para realizar ciertas acciones
en argumentos llamados operandos.

Los operandos pueden ser tanto valores como variables. Hemos


encontrado operadores varias veces en ejemplos anteriores, por ejemplo, el
símbolo de asignación = o la palabra clave typeof.

Los operadores se pueden categorizar de varias maneras. Se distinguen,


por ejemplo, por el número de operandos sobre los que trabajan. El
operador de suma + es un operador binario típico (utiliza dos operandos),
mientras que el operador typeof es unario (utiliza un solo operando).

En JavaScript, también hay un operador ternario (que opera en tres


operandos), sobre el cual hablaremos brevemente en un momento.

Podemos diferenciar entre operadores de prefijo (que ocurren antes del


operando), operadores de postfijo (después del operando) y operadores
de infijo (entre operandos). Sin embargo, es común categorizar a los
operadores según el contexto en el que se usan: así tenemos de
asignación; aritmético; lógico; u operadores condicionales.
Revisaremos más a fondo los operadores básicos de JavaScript de acuerdo
con esta clasificación.

El mismo símbolo puede interpretarse como un operador diferente según


el contexto. En JavaScript, el símbolo + es un ejemplo. Si los operandos
son números, el uso de este operador hará que el intérprete calcule su
suma (es un operador de suma, clasificado como aritmético). Sin embargo,
si los operandos son cadenas, el mismo símbolo se tratará como un
operador de concatenación y el intérprete intentará unir ambas cadenas de
caracteres.

Operadores de asignación

Comencemos con los operadores de asignación. En este grupo se


encuentran los operadores que permiten asignar valores a variables y
constantes. El operador de asignación básico es el signo igual =, que ya
hemos visto muchas veces en los ejemplos. Este operador asigna el valor
del operando derecho al operando izquierdo.

const name = "Alice";


console.log(name); // -> Alice

Si aparecen varios operadores de asignación en una secuencia, se aplica el


orden de derecha a izquierda. Entonces la secuencia:

let year = 2050;


let newYear = year = 2051;

significa lo mismo que:

let year = 2050;


year = 2051;
let newYear = year;

Además del operador de asignación básico, también hay operadores de


asignación conectados a operadores aritméticos, lógicos y de cadena.
Volveremos a ellos cuando hablemos de las otras categorías de operadores

Operadores aritméticos
Los operadores aritméticos expresan operaciones matemáticas y aceptan
valores numéricos y variables. Todos los operadores aritméticos, excepto la
suma, intentarán convertir implícitamente los valores al tipo Number
antes de realizar la operación.

El operador de suma convertirá todo a String si alguno de los operandos es


de tipo String, de lo contrario los convertirá a Number como el resto de los
operadores aritméticos. El orden de las operaciones se respeta en
JavaScript como en matemáticas, y podemos usar paréntesis como en
matemáticas para cambiar el orden de las operaciones si es necesario.

En general, es un buen hábito usar paréntesis para forzar la precedencia y


el orden de las operaciones, no solo las aritméticas. La precedencia de las
operaciones realizadas por el intérprete no siempre será tan intuitiva como
la precedencia de las operaciones aritméticas conocidas de las
matemáticas.

console.log(2 + 2 * 2); // -> 6


console.log(2 + (2 * 2)); // -> 6
console.log((2 + 2) * 2); // -> 8

Los operadores aritméticos binarios básicos son la suma +, la resta -, la


multiplicación *, la división /, residuo de la división % y la potencia **. Su
funcionamiento es análogo a lo que sabemos por las matemáticas, y la
forma más fácil de rastrearlos es usar un ejemplo:

const x = 5;
const y = 2;

console.log("suma: ", x + y); // -> 7


console.log("resta: ", x - y); // -> 3
console.log("multiplicación: ", x * y); // -> 10
console.log("división: ", x / y); // -> 2.5
console.log("residuo de la división: ", x % y); // -> 1
console.log("potencia: ", x ** y); // -> 25

Operadores aritméticos unarios

También existen varios operadores aritméticos unarios (que operan en un


solo operando). Entre ellos se encuentran los operadores de más + y
menos -.

Ambos operadores convierten los operandos al tipo Number, mientras que


el operador de menos (negativo) lo niega.

let str = "123";


let n1 = +str;
let n2 = -str;
let n3 = -n2;
let n4 = +"abcd";

console.log(`${str} : ${typeof str}`); // -> 123 : string


console.log(`${n1} : ${typeof n1}`); // -> 123 : number
console.log(`${n2} : ${typeof n2}`); // -> -123 : number
console.log(`${n3} : ${typeof n3}`); // -> 123 : number
console.log(`${n4} : ${typeof n4}`); // -> NaN : number

Operadores unarios de incremento y decremento

Entre los operadores aritméticos, también tenemos a nuestra disposición


los operadores de incremento ++ y decremento -- unario, tanto en
versiones de prefijo como de sufijo. Nos permiten aumentar (incrementar) o
disminuir (decrementar) el valor del operando en 1.

Estos operadores en la versión de sufijo (es decir, el operador está en el


lado derecho del operando) realizan la operación cambiando el valor de la
variable, pero devuelven el valor antes del cambio. La versión de prefijo del
operador (es decir, el operador se coloca antes del operando) realiza la
operación y devuelve el nuevo valor.

Probablemente, la forma más fácil de entenderlo es usar un ejemplo del


editor.

Esto sucede porque la línea de código:

console.log(n1++);

es interpretada como:

console.log(n1);
n1 = n1 + 1;

mientras que la línea:

console.log(++n1);

significa lo mismo que:


n1 = n1 + 1;
console.log(n1);

Toma en cuenta que el tipo Number es un tipo de dato punto flotante, lo


que significa que los resultados de algunas de las operaciones pueden ser
imprecisos.

console.log(0.2 + 0.1); // 0.30000000000000004


console.log(0.2 * 0.1); // 0.020000000000000004
console.log(0.3 / 0.1); // 2.9999999999999996

Estos son artefactos de la aritmética de punto flotante. El número será


preciso para enteros hasta 252, pero las fracciones pueden no ser tan
precisas, ya que muchas fracciones son imposibles de representar
directamente en formato binario. Discutiremos cómo mitigar esto en un
momento cuando presentemos operadores de comparación.

let n1 = 10;

let n2 = 10;

console.log(n1); // -> 10

console.log(n1++); // -> 10

console.log(n1); // -> 11

console.log(n2); // -> 10

console.log(++n2); // -> 11

console.log(n2); // -> 11

let n3 = 20;

let n4 = 20;
console.log(n3); // -> 20

console.log(n3--); // -> 20

console.log(n3); // -> 19

console.log(n4); // -> 20

console.log(--n4); // -> 19

console.log(n4); // -> 19

Operadores de Asignación Compuesta

Los operadores aritméticos binarios se pueden combinar con


el operador de asignación, dando como resultado la asignación de
suma +=, la asignación de resta -=, la asignación de la multiplicación *=, la
asignación de la división /=, la asignación del residuo %= y la asignación
de potencia **=.

Cada uno de estos operadores toma un valor de la variable a la que se va a


realizar la asignación (el operando izquierdo) y lo modifica realizando una
operación aritmética utilizando el valor del operando derecho. El nuevo
valor se asigna al operando izquierdo. Por ejemplo, el siguiente fragmento
de código:

x += 100;

puede ser escrito de la forma:

x = x + 100;

Por lo tanto, no debería ser difícil entender cómo funciona el siguiente


ejemplo:

let x = 10;

x += 2;
console.log(x); // -> 12
x -= 4;
console.log(x); // -> 8
x *= 3;
console.log(x); // -> 24
x /= 6;
console.log(x); // -> 4
x **= 3;
console.log(x); // -> 64
x %= 10;
console.log(x); // -> 4

Operadores Lógicos

Los operadores lógicos funcionan con valores de tipo booleano


(true o false). Por ahora, podemos suponer que funcionan con operandos
de este tipo y devuelven valores de este tipo únicamente. JavaScript nos
proporciona tres de estos operadores:

 una conjunción, es decir, AND (Y) lógico (símbolo &&)


 una disyunción, es decir, OR (O) lógico (símbolo ||)
 una negación, es decir, NOT (NO) lógico (símbolo !)

Su significado es el mismo que en la lógica matemática, y si no estás


seguro de cómo funcionan, es más fácil explicarlos con base en
enunciados lógicos.

Empecemos con la conjunción. Este es un operador binario que devuelve


verdadero si ambos operandos son verdaderos. Usando enunciados
lógicos, podemos imaginar un enunciado que consta de dos sentencias
simples conectadas por un AND, por ejemplo:

Londres es una ciudad AND (Y) Londres está en Gran Bretaña.

Ambas sentencias son verdaderas en este caso, y después de combinarlas


con AND, se crea un enunciado que también es verdadero. Si alguna de
estas sentencias fuera falsa (o ambas fueran falsas), el enunciado completo
también sería falso, por ejemplo:

Londres es una ciudad AND (Y) Londres está en Islandia.

En código JavaScript es tan simple como esto:

console.log(true && true); // -> true


console.log(true && false); // -> false
console.log(false && true); // -> false
console.log(false && false); // -> false

En el caso de una disyunción que también es un operador binario, basta


que uno de los operandos sea verdadero para que el operador devuelva
verdadero. Volviendo a nuestro ejemplo con enunciados lógicos, usemos
un enunciado formada por dos sentencias conectadas por un operador OR,
por ejemplo:

Londres es una ciudad OR (O) Londres está en Islandia.

Puede que el enunciado no parezca demasiado elocuente o sensato, pero


desde el punto de vista de la lógica, es bastante correcto. Basta que una de
las sentencias sea verdadera, para que todo el enunciado también sea
verdadero. Si ambas sentencias son falsas, entonces el enunciado también
será falso, por ejemplo:

Londres es un pueblo OR (O) Londres está en Islandia.

Veamos cómo se ve en JavaScript:

console.log(true || true); // -> true


console.log(true || false); // -> true
console.log(false || true); // -> true
console.log(false || false); // -> false

El operador de negación es unario y cambia el valor lógico del operando a


su opuesto, es decir, falso a verdadero y verdadero a falso. Usando
sentencias lógicas, podemos presentarlo con la negación NOT. Tomemos
como ejemplo un enunciado simple que es verdadero:

Londres es una ciudad.

Al agregarle la negación, cambiamos su valor a falso:

Londres NO es una ciudad.

Del mismo modo, funcionará al revés, cambiando una frase falsa por una
verdadera. En el código, se verá aún más simple:

console.log(!true); // -> false


console.log(!false); // -> true

Podemos, por supuesto, conectar tantos de estos operadores como


necesitemos, creando "frases" más complejas. Como en el caso de los
operadores aritméticos, aquí se determina la secuencia de acciones. La
prioridad más alta es la negación !, luego la conjunción &&, y finalmente
la disyunción ||. Por supuesto, la precedencia se puede cambiar mediante
paréntesis.

const a = false;
const b = true;
const c = false;
const d = true;

console.log(a && b && c || d); // -> true


console.log(a && b && (c || d)); // -> false

Operadores lógicos y valores no booleanos

Mientras los operandos sean del tipo Boolean, podemos ver fácilmente lo
que se devolverá. Pero esos operadores también se pueden usar con
diferentes tipos de datos. El caso más fácil es el NO lógico. Primero, el
operando se convierte temporalmente a un valor booleano (según las reglas
explicadas en el capítulo anterior) y solo entonces se trata con la acción de
operador adecuada (es decir, un valor verdadero se convierte en falso y
viceversa). Por lo tanto, el operador NOT siempre devolverá falso o
verdadero. A menudo, se utiliza la doble negación para convertir cualquier
tipo a Boolean.

let nr = 0;
let year = 1970;
let name = "Alice";
let empty = "";

console.log(!nr); // -> true


console.log(!year); // -> false
console.log(!name); // -> false
console.log(!empty); // -> true

console.log(!!nr); // -> false


console.log(!!name); // -> true
Esto es ligeramente diferente para los operadores lógicos binarios (es decir,
AND y OR). No devuelven un valor booleano. En realidad, devuelven su
primer o segundo operando. El operador AND devolverá el primer operando
si se evalúa como falso y el segundo operando en caso contrario. El
operador OR devolverá su primer operando si se evalúa como verdadero y
el segundo operando en caso contrario. La evaluación es simplemente un
intento de convertir un operando en un valor de tipo booleano
(nuevamente, de acuerdo con las reglas aprendidas en el capítulo anterior).

console.log(true && 1991); // -> 1991


console.log(false && 1991); // -> false
console.log(2 && 5); // -> 5
console.log(0 && 5); // -> 0
console.log("Alice" && "Bob"); // -> Bob
console.log("" && "Bob"); // -> empty string

console.log(true || 1991); // -> true


console.log(false || 1991); // -> 1991
console.log(2 || 5); // -> 2
console.log(0 || 5); // -> 5
console.log("Alice" || "Bob"); // -> Alice
console.log("" || "Bob"); // -> Bob

Operadores lógicos y valores no booleanos - continuación

Ambos operadores también utilizan la evaluación de cortocircuito.

Entonces, si el primer operando de AND (Y) es false, se devolverá y no se


realizará ninguna otra verificación.

Por el contrario, si el primer operando de OR (O) es true, se devolverá y no


se realizará ninguna otra comprobación. Esto acelera la ejecución del
código, pero tiene un efecto secundario visible en este ejemplo:

let x = 0;
let y = 0;
console.log(x++ && y++); // -> 0
console.log(x); // -> 1
console.log(y); // -> y == 0

La instrucción y++ nunca se ejecutará, lo que puede resultar confuso.

Los operadores lógicos se suelen utilizar junto con los operadores


condicionales, y son especialmente útiles en instrucciones
condicionales (toma de decisiones) y en bucles (condiciones de fin de
ciclo). Puedes aprender sobre su aplicación práctica en las secciones sobre
instrucciones condicionales y bucles.

Operadores de Asignación Compuesta

Al igual que los operadores aritméticos, los operadores lógicos


binarios se pueden usar en combinación con el operador de asignación,
creando una asignación AND lógica &&= y una asignación OR lógica ||=.

Debería ser fácil imaginar cómo funcionan. En el caso del operador AND,
podemos comprobarlo con el siguiente ejemplo:

let a = true;
console.log(a); // -> true
a &&= false;
console.log(a); // -> false

La instrucción a &&= false significa exactamente lo mismo que a = a &&


false.

Podemos preparar un ejemplo similar para operaciones OR:

let b = false;
console.log(b); // -> false
b ||= true;
console.log(b); // -> true

Esta vez, la operación b ||= true se interpreta como b = b || true.

Tareas

Tarea 1

Operadores aritméticos

Completa los operadores que faltan para obtener el resultado esperado (reemplaza el guión
bajo con el operador apropiado):

console.log(2 _ 3 _ 1); // salida 7


console.log(2 _ 4); // salida 16
console.log(5 _ 1); // salida 5
console.log(8 _ 2 _ 5 _ 2); // salida 39
Ejemplo

console.log(2 * 3 + 1);

console.log(2 ** 4);

console.log(5 * 1);

console.log(8 ** 2 - 5 ** 2);

Tarea 2

Operadores de comparación

Completa los operadores de comparación que faltan de tal manera que todas las expresiones
resulten true - verdaderas (reemplaza el guión bajo con el operador apropiado):

console.log(4 * 5 _ 20);
console.log(6 * 5 _ "30");
console.log(-17 _ 0);
console.log(25 _ 1);
console.log(2 + 2 * 2 _ 4);
Ejemplo

console.log(4 * 5 === 20);

console.log(6 * 5 == "30");

console.log(-17 < 0);

console.log(25 > 1);

console.log(2 + 2 * 2 != 4);

Tarea 3

Operadores Lógicos

Completa los operadores de comparación que faltan de tal manera que todas las expresiones
resulten true - verdaderas (reemplaza el guión bajo con el operador apropiado):

console.log(true _ false);
console.log(false _ false);
console.log(false _ false _ true);
console.log(true _ false _ false && true);
Ejemplo

console.log(true || false);

console.log(false || ! false);

console.log(false || false || true);

console.log(true || false && false && true);

Operadores de cadenas
El único operador en este grupo es la concatenación + . Este operador convertirá todo a una cadena si
alguno de los operandos es de tipo String. Finalmente, creará una nueva cadena de caracteres,
adjuntando el operando derecho al final del operando izquierdo.

let greetings = "Hi";

console.log(greetings + " " + "Alice"); // -> Hi Alice

let sentence = "Happy New Year ";

let newSentence = sentence + 10191;

console.log(newSentence); // -> Happy New Year 10191

console.log(typeof newSentence); // -> string

Operadores de asignación compuesta

Probablemente puedas adivinar que este operador también se puede usar junto con el operador de
reemplazo. Su funcionamiento es tan intuitivo que solo verémos un ejemplo sencillo:

let sentence = "Happy New ";

sentence += "Year ";

sentence += 10191;
console.log(sentence); // -> Happy New Year 10191

Operadores de comparación
Los operadores de comparación se utilizan para verificar la igualdad o desigualdad de valores. Todos los
operadores de comparación son binarios y todos devuelven un valor lógico que representa el
resultado de la comparación, true o false.

Al igual que con otros operadores, JavaScript intentará convertir los valores que se comparan si son de
diferentes tipos. Tiene sentido verificar la igualdad, o cuál es mayor, usando la representación numérica,
y JavaScript en la mayoría de los casos convertirá los tipos a Number antes de la comparación. Hay dos
excepciones a esto, las cadenas y el operador de identidad (igualdad estricta). Las cadenas se
comparan char por char (especificamente carácter Unicode por carácter Unicode usando sus valores).

Para verificar si los operandos son iguales, podemos usar el operador de identidad (igualdad
estricta) === o el operador de igualdad <código style="box-sizing: border-box;">==</código>.

El primero es más restrictivo y, para devolver verdadero, los operandos deben ser idénticos (es decir,
deben ser iguales y del mismo tipo).

console.log(10 === 5); // -> false


console.log(10 === 10); // -> true
console.log(10 === 10n); // -> false
console.log(10 === "10"); // -> false
console.log("10" === "10"); // -> true
console.log("Alice" === "Bob"); // -> false
console.log(0 === false); // -> false
console.log(undefined === false); // -> false

El operador de igualdad requiere que solo los valores sean iguales y sus tipos no se comparan. Entonces,
si los operandos son de diferentes tipos, el intérprete intentará convertirlos en números, por
ejemplo, true se convertirá en 0, true a 1, indefinido a NaN, null a 0, 10n
a 10 y "123" a 123, etc.

Toma en cuenta que si alguno de los operandos tiene un valor NaN (o se ha convertido a NaN, por
ejemplo, con undefined), el operador de igualdad devolverá false.

console.log(10 == 5); // -> false


console.log(10 == 10); // -> true
console.log(10 == 10n); // -> true
console.log(10 == "10"); // -> true
console.log("10" == "10"); // -> true
console.log("Alice" == "Bob"); // -> false
console.log(0 == false); // -> true
console.log(undefined == false); // -> false
console.log(NaN == NaN); // -> false

¡Recuerda! Utiliza el operador de identidad a menos que permitas intencionalmente una


comparación positiva entre los diferentes tipos de datos.

También hay operadores complementarios a los que acabamos de demostrar: el operador de no


identidad !== y el operador de desigualdad !=. El primero devuelve true si los operandos no son
idénticos, es decir, son iguales pero de diferente tipo, o simplemente son diferentes. El segundo
devuelve true si los operandos son diferentes.

console.log(10 !== 5); // -> true


console.log(10 !== 10); // -> false
console.log(10 !== 10n); // -> true
console.log(10 !== "10"); // -> true
console.log("10" !== "10"); // -> false
console.log("Alice" !== "Bob"); // -> true
console.log(0 !== false); // -> true
console.log(undefined !== false); // -> true
console.log(10 != 5); // -> true
console.log(10 != 10); // -> false
console.log(10 != 10n); // -> false
console.log(10 != "10"); // -> false
console.log("10" != "10"); // -> false
console.log("Alice" != "Bob"); // -> true
console.log(0 != false); // -> false
console.log(undefined != false); // -> true
console.log(NaN != NaN); // -> true

Operadores de comparación - continuación


También tenemos operadores que nos permiten comprobar si uno de los operandos es mayor
que >, menor que <, mayor o igual que >=, y menor o igual que <=. Estos operadores
funcionan en cualquier tipo de operando, pero tiene sentido usarlos solo en números o valores
que se convertirán correctamente en números.

console.log(10 > 100); // -> false


console.log(101 > 100); // -> true
console.log(101 > "100"); // -> true

console.log(101 < 100); // -> false


console.log(100n < 102); // -> true
console.log("10" < 20n); // -> true

console.log(101 <= 100); // -> false


console.log(10 >= 10n); // -> true
console.log("10" <= 20); // -> true
También puedes usarlos para comparar cadenas que no representan números, pero el
algoritmo de esta comparación es bastante complejo y la comparación en sí no es muy útil. A
modo de simplificación, los caracteres individuales de ambas cadenas se prueban en las
mismas posiciones. Se supone que los valores de los caracteres individuales corresponden a
sus posiciones en el alfabeto (la letra b tiene un valor más alto que la letra a). Las letras
mayúsculas tienen valores más bajos que las letras minúsculas y los dígitos tienen valores
incluso más bajos.

console.log("b" > "a"); // -> true


console.log("a" > "B"); // -> true
console.log("B" > "A"); // -> true
console.log("A" > "4"); // -> true
console.log("4" > "1"); // -> true

console.log("ab1" < "ab4"); // -> true


console.log("ab4" < "abA"); // -> true
console.log("abB" < "aba"); // -> true
console.log("aba" < "abb"); // -> true

console.log("ab" < "ab4"); // -> true

Nota: el símbolo => existe en JavaScript, pero no es un operador; lo usamos en la


construcción de funciones de flecha.

Otros operadores
La lista de operadores en JavaScript es mucho más larga, pero muchos de ellos no serían particularmente
útiles en esta etapa de aprendizaje, como los operadores bit a bit, que operan en bits únicos de
operandos. Sin embargo, vale la pena mencionar algunos operadores más, algunos de los cuales ya
aparecieron en ejemplos anteriores.

typeof

Ya presentamos el operador typeof cuando discutimos los tipos de datos. Es un operador unario, que
comprueba el tipo de operando (puede ser una variable o un literal). El operador devuelve una cadena
con el nombre del tipo, como "boolean" o "number".

Si deseas refrescar tus conocimientos sobre este operador, vuelve a la sección sobre tipos de datos.

let year = 10191;

console.log(typeof year); // -> number

console.log(typeof false); // -> boolean


instanceof

El operador instanceof apareció mientras discutíamos los arreglos. Es un operador binario que
verifica si un objeto (operando izquierdo) es del tipo (operando derecho). Dependiendo del resultado de
la prueba, devuelve verdadero o falso.

Durante este curso, la utilidad de este operador se limita a probar si una variable contiene un arreglo.

let names = ["Patti", "Bob"];

let name = names[0];

console.log(names instanceof Array); // -> true

console.log(name instanceof Array); // -> false

delete

El operador unario delete se introdujo al hablar de los objetos. Te permite eliminar un campo
seleccionado del objeto cuyo nombre se indica con un operando.

let user = {

name: "Alice",

age: 38

};

console.log(user.age); // -> 38

delete user.age;

console.log(user.age); // -> undefined


ternary

El último de los operadores discutidos es bastante inusual, porque es el único operador que usa tres
operandos. Es un operador condicional. Según el valor del primer operando (verdadero o falso), se
devuelve el valor del segundo o tercer operando, respectivamente. Este operador se usa con mayor
frecuencia para colocar uno de los dos valores en la variable dependiendo de una determinada condición.
Volveremos al operador cuando hablemos del condicional if, pero aquí daremos solo un ejemplo simple
de su uso. Los tres operandos están separados entre sí por ? (el primero del segundo) y : (el segundo del
tercero).

console.log(true ? "Alice" : "Bob"); // -> Alice

console.log(false ? "Alice" : "Bob"); // -> Bob

Cada uno de estos operandos puede ser una expresión que debe calcularse. En el siguiente ejemplo, el
primer operando es una comparación de dos números usando un operador de comparación. El resultado
de la comparación será falso, que será utilizado por el operador condicional (ternary). Aquí llegamos a
un problema importante sobre la precedencia de los operadores y el orden de ejecución. En un momento,
diremos algunas palabras más al respecto.

let name = 1 > 2 ? "Alice" : "Bob";

console.log(name); // -> Bob

Precedencia
Prácticamente en todos los ejemplos donde presentamos la operación de operadores sucesivos, seguimos
instrucciones en las que se usaba un operador. En realidad, por lo general se utilizan múltiples
operadores simultáneamente. En este punto surge una pregunta bastante importante: ¿en qué orden las
realizará el intérprete? Por supuesto, esto afectará el resultado final de los operadores, por lo que vale la
pena tener esto en cuenta al escribir las instrucciones.

let a = 10;
let b = a + 2 * 3;
let c = a + 2 < 20 - 15;
console.log(a); // -> 10
console.log(b); // -> 16
console.log(c); // -> false

En la segunda línea del ejemplo (declaración de la variable b), los operadores se ejecutan en el orden que
conocemos de las matemáticas. Primero se realiza la multiplicación, luego la suma y al final se le asigna
el valor resultante a la variable. En la tercera línea (declaración de la variable c) el asunto se complica un
poco más. Primero se calcula la suma de la variable a y el número 2, luego la suma de los números 20 y
15, y ambos resultados se comparan con el operador lógico (menor que) y se coloca el resultado en la
variable c.
El intérprete de JavaScript utiliza dos propiedades de operador para determinar la secuencia de
operaciones: precedencia y asociatividad. La precedencia se puede tratar como una prioridad, con
algunos operadores que tienen la misma precedencia (por ejemplo, suma y resta). La asociatividad le
permite especificar el orden de ejecución si hay varios operadores con las mismas prioridades uno al
lado del otro.

La precedencia se puede presentar como un valor numérico: cuanto mayor sea el valor, mayor será la
prioridad de la operación. Si, por ejemplo, un operador OP1 tiene una precedencia menor que OP2 ,
entonces la instrucción:

a OP1 b OP2 c

se ejecutará de la siguiente manera: primero, OP2 se ejecutará en los operandos b y c, entonces OP1 se
ejecutará en el operando izquierdo a y el operando derecho, resultante de OP2 . Entonces la instrucción
podría presentarse en la forma:

a OP1 ( b OP2 c)

Si realizamos las mismas operaciones (u operaciones diferentes pero con la misma precedencia), el
intérprete utiliza la asociatividad para determinar el orden de las operaciones. Los operadores pueden
tener una asociatividad izquierda específica (orden de izquierda a derecha) o asociatividad derecha
(orden de derecha a izquierda). Supongamos que en nuestro ejemplo, el operador OP1 tiene
asociatividad por la izquierda:

a OP1 b OP2 c

En tal situación, la operación OP1 en los operandos a y b se realizará primero, seguida de una segunda
operación OP1 sobre el resultado recibido y el operando c. Teniendo en cuenta que se trata de
asociatividad por la izquierda, podríamos escribir la instrucción de la siguiente forma:

(a OP1 b) OP2 c

Se deduce que sería apropiado conocer no solo la precedencia de todos los operadores, sino también su
asociatividad. Esto puede parecer un poco abrumador, teniendo en cuenta la cantidad de operadores.
Afortunadamente, suele ser suficiente conocer las propiedades de los operadores más básicos y usar
paréntesis en situaciones dudosas. Los paréntesis te permiten imponer el orden de las operaciones, como
en matemáticas. Toma esto en cuenta cuando veas la tabla en la siguiente página. Contiene una lista de
operadores que ya conocemos con su precedencia y asociatividad, por lo que es bastante grande.
Absolutamente no tienes que recordar todo si puedes usar paréntesis para agrupar operaciones.

Precedencia - continuación
Precedencia Operador Asociatividad Símbolo

14 Agrupamiento n/a (…)

13 Acceso al campo ⇒ ….…

12 Llamada de función ⇒ …(…)


Precedencia Operador Asociatividad Símbolo

Incremento postfijo n/a … ++


11
Decremento postfijo n/a … --

NOT (NO) lógico ⇐ !…

Signo de más unario ⇐ +…

Negación unaria ⇐ -…

11 Incremento prefijo ⇐ ++ …

Decremento prefijo ⇐ -- …

Tipo de dato ⇐ typeof …

Eliminar ⇐ delete …

9 Exponenciación ⇐ … ** …

Multiplicación ⇒ …*…

8 División ⇒ …/…

Residuo ⇒ …%…

Suma ⇒ …+…
7
Resta ⇒ …-…

Menor que ⇒ …<…

Menor o igual que ⇒ … <= …

6 Mayor que ⇒ …>…

Mayor o igual que ⇒ … >= …

instancia de ⇒ … instanceof …

5 Igualdad ⇒ … == …

Desigualdad ⇒ … != …

Igualdad estricta ⇒ … === …


Precedencia Operador Asociatividad Símbolo

Desigualdad estricta ⇒ … !== …

4 AND (Y) lógico ⇒ … && …

3 OR (O) lógico ⇒ … || …

2 Condicional (ternary) ⇐ …?…:…

…=…
… += …
1 Asignación ⇐ … *= …

… y otros operadores de asignación

Algunas palabras de explicación sobre la tabla.

Una flecha en la columna de asociatividad que apunta hacia el lado derecho significa asociatividad de
izquierda a derecha, mientras que hacia el lado opuesto significa asociatividad de derecha a izquierda.

La abreviatura n/a significa no aplicable, porque en algunos operadores el término asociatividad no tiene
sentido.

Al principio de la tabla, hay tres operadores que pueden necesitar más explicación:

 Agrupar es simplemente usar paréntesis. Tienen prioridad sobre los demás operadores, por lo
que podemos usarlos para forzar la ejecución de operaciones para que tengan prioridad;

 Acceso al campo (acceso al miembro) es el operador que se usa en la notación de puntos, que
es cuando se accede a un campo de objeto seleccionado. Tiene prioridad sobre otros operadores
(excepto los paréntesis), por ejemplo, la instrucción:

let x = myObject.test + 10; significa que el valor del campo test del
objeto myObject se obtendrá primero, luego le sumaremos un valor de 10, y el resultado irá a
la variable x;

 La precedencia llamada de función nos dice que si llamamos a una función, esta acción tendrá
prioridad sobre otras operaciones, excepto la agrupación entre paréntesis y el operador de
acceso al campo (puntos). Así que en el ejemplo:

let y = 10 + myFunction() ** 2; myFunction será llamada primero, el resultado


devuelto será elevado a potencia 2, y solo entonces sumaremos 10 al total y guardaremos el
resultado en la variable y.
Recuerda, sin embargo, si tienes alguna duda, usa paréntesis para ordenar la precedencia de los
operadores utilizados. Te permiten organizar incluso las instrucciones más confusas que se te ocurran.

let a, b;

b = (a = (20 + 20) * 2) > (3 ** 2);

console.log(a); // -> 80

console.log(b); // -> true

El uso de paréntesis no solo fuerza el orden de las acciones, sino que también aumenta la legibilidad del
código (la persona que lo lee no tiene que preguntarse qué y en qué orden se hará).

La lista completa de operadores y propiedades se puede encontrar en las


páginas precedencia y operadores.

Resumen de Sección
En este capítulo, hemos introducido algunos operadores nuevos (por ejemplo, operadores
lógicos) y también hemos consolidado nuestro conocimiento sobre algunos que ya conocemos
(por ejemplo, operadores de asignación). Junto con los operadores, han aparecido nuevos
términos que describen sus propiedades: precedencia y asociatividad.

Es probable que la cantidad de información nueva que ha surgido, especialmente en relación


con la precedencia de los operadores, parezca un poco desalentadora. No te preocupes, es
bastante normal. De hecho, probablemente no haya muchos programadores de JavaScript
que puedan configurar correctamente todos los operadores de acuerdo con sus prioridades.

Afortunadamente, tenemos paréntesis para ayudar, lo que nos facilita forzar la prioridad de las
operaciones.

Los operadores aparecerán en todos los ejemplos hasta el final del curso, por lo que poco a
poco irás consolidando los conocimientos adquiridos, que seguramente se convertirán en un
todo lógico y coherente con el tiempo.

Interacción con el usuario


La mayoría de los programas con los que tratamos a diario dependen de la interacción con el usuario. El
usuario ingresa datos, hace elecciones y confirma las opciones dadas por el programa. Gracias a esta
interacción, el programa puede empezar a utilizar los datos que le proporciona el usuario durante su
ejecución (estos datos no se conocían cuando se inició el programa, ni se sabía cuando se escribió). En
segundo lugar, el programa puede realizar ciertas acciones condicionalmente, en otras palabras, el
usuario influye no solo en los datos, sino también en su ejecución.
Un ejemplo simple es el programa calculadora. El usuario no solo ingresa datos (por ejemplo, los
números 10 y 20), sino que también decide qué hacer con ellos (por ejemplo, sumar).

En el caso de JavaScript, la interacción con el usuario es un tema bastante complejo. Esto es por varias
razones. ¿Recuerdas que podemos escribir programas en este lenguaje tanto para usar en el navegador
(lado del cliente) como para realizar ciertas acciones en el lado del servidor? Esta división influye en la
interacción potencial. Los programas de JavaScript escritos con node.js para servidores generalmente no
requieren tal interacción. Los ejecutamos desde el nivel de la consola, a menudo sin un entorno gráfico.

La mayoría de las veces, el usuario da ciertas opciones (por ejemplo, datos de ruta) al programa que se
ejecuta de esta manera en forma de archivo de configuración y/o como llamada de argumentos. Los
datos para ejecutar el programa se toman luego de archivos de disco, bases de datos, servicios de red,
etc. Es muy raro que estos programas de consola necesiten ingresar algunos datos mientras se ejecuta el
programa. Si es necesario, se indica mediante la aparición de información apropiada en la consola, en la
que debes ingresar algunos datos.

Esto es definitivamente diferente para las aplicaciones del lado del cliente. En la mayoría de los casos,
requieren una interacción continua entre el usuario y el programa. Utilizándolos, podemos hacer clic en
botones, ingresar valores en formularios, seleccionar datos de listas desplegables, etc. El problema es
que prácticamente todos los elementos que se utilizan para este fin son componentes HTML. Usarlos
puede no ser muy difícil, pero requiere al menos una comprensión profunda de los conceptos básicos del
DOM (Document Object Model) que se utiliza en las páginas web y los conceptos básicos del propio
HTML

A continuación se muestra un ejemplo de una página HTML que utiliza dos elementos para la
interacción, para lo cual se utiliza código JavaScript:

<!DOCTYPE html>
<html>
<head></head>
<body>
<input id="myId" type="text"></input>
<button
onclick="console.log(document.getElementById('myId').value)">Obtener
Texto</button>
</body>
</html>

Ejecuta este código en el editor, asegurándote de que esté en la ventana dedicada a HTML (pestaña
index.html). El elemento <input> es un campo de entrada donde puedes ingresar cualquier texto. En
nuestro caso, le hemos dado a este elemento el identificador myId. El elemento <button>, como
puedes adivinar, corresponde a... un botón. Usando el atributo onClick, hemos indicado que si se hace
clic en el botón, se ejecutará una parte del código JavaScript. En este código, nos referimos al objeto del
documento (un fragmento del modelo DOM), que representa nuestro sitio web. Buscamos el elemento
con el identificador myId, recuperamos su valor (es decir, el texto ingresado) e imprimimos el resultado
en la consola.
Como puedes ver, la idea es relativamente simple, pero tanto el modelo DOM como el lenguaje HTML
están definitivamente más allá del alcance del curso actual. Entonces, ¿cómo podemos comunicarnos
con el usuario si no utilizamos estos mecanismos estándar?

Hay otra solución. Toma en cuenta que no se usa en las aplicaciones web modernas, pero te permite
brindar fácilmente al usuario la oportunidad de ingresar datos o tomar ciertas decisiones. Lo usaremos en
este curso para la comunicación normal, en lugar de como un elemento que encontrarás útil en la
programación real. La solución es usar cuadros de diálogo.

Cuadros de diálogo
Los cuadros de diálogo son partes integrales de los navegadores web y están disponibles en
casi todos ellos, incluso en los más antiguos. Todos ellos son ventanas emergentes (o
ventanas modales), lo que significa que cuando se muestra el cuadro de diálogo, no es
posible interactuar con la página web en sí hasta que se cierra este cuadro de diálogo.

Este inconveniente cuando la ventana emergente está visible es una de las razones por las
que no debemos abusar de ellos. Están perfectamente bien para el proceso de aprendizaje, y
en algunos casos excepcionales en los que se debe mostrar información importante o es
obligatoria alguna entrada del usuario, pero se deben evitar en otras circunstancias. Tenemos
tres cuadros de diálogo disponibles para usar.

Cuadro de diálogo de alerta

El cuadro de diálogo de alerta es el más simple de los tres, y para mostrar un cuadro de
alerta, necesitamos llamar a un método llamado alert() . El método alert acepta un
parámetro opcional: el texto que se mostrará. El método alert es un método de la ventana
de objetos, pero por conveniencia, se puede usar sin necesidad de escribir window.alert ,
por lo que ambas formas son correctas y se pueden ver en aplicaciones reales. El objeto de
ventana es una generalización de la ventana o pestaña del navegador, y le da al desarrollador
acceso a datos relacionados con el estado de esta ventana (por ejemplo, cuánto se desplaza
hacia abajo la página, porque el objeto de consola es parte del objeto de ventana) y también
algunos métodos para controlar este estado.

alert("¡Hola, Mundo!")

window.alert("¡Hola, Mundo!, por segunda ocasión");

alert(4 * 7);

alert(true);

alert("texto 1", "texto 2"); // solo "texto 1" será mostrado


Al igual que console.log , podemos insertar cualquier valor en el método alert y se
convertirá en una cadena. La diferencia es que podemos poner un número arbitrario de
parámetros a console.log , mientras que con la alerta debemos poner solo uno (o cero, ya
que es un parámetro opcional).

La ventana de alert estará visible hasta que el usuario haga clic en el botón Aceptar visible
en la ventana emergente. La ejecución del código se detendrá hasta que se cierre el cuadro
de diálogo.

Cuadros de diálogo de confirmación


El segundo cuadro de diálogo se denomina cuadro de diálogo confirm. Al igual que alert, es un
método que acepta un parámetro opcional: un mensaje que se mostrará. La diferencia
entre alert y confirm es que el cuadro de diálogo confirm muestra dos botones, el botón Aceptar y
el botón Cancelar. Dependiendo del botón presionado por el usuario, el método confirm devuelve un
valor booleano. Se devuelve verdadero cuando el usuario cierra el cuadro de diálogo con el botón
Aceptar y se devuelve falso cuando el usuario presiona el botón Cancelar.

let decision = window.confirm("¿Está bien?");


console.log(decision);

Los valores verdadero o falso, devueltos por el método confirm como resultado de la decisión del
usuario, permiten la ejecución condicional de algunas acciones del programa. En el siguiente ejemplo,
también hemos utilizado un operador condicional aprendido recientemente:

let remove = confirm("¿Eliminar todos los datos?");


let message = remove ? "Eliminando Datos" : "Cancelado"

console.log(message);

Cuadros de diálogo de aviso (prompt)


El último de los cuadros de diálogo es el cuadro de diálogo prompt. Es un desarrollo posterior
de la ventana emergente confirm. Al igual que el cuadro de diálogo confirm, contiene los
botones Aceptar y Cancelar, pero también contiene un campo de texto de una sola línea que
permite al usuario ingresar texto.

Al igual que con otros cuadros de diálogo, el prompt acepta un parámetro opcional como un
mensaje que se mostrará. El prompt también acepta un segundo parámetro opcional, que es
el valor predeterminado del campo de texto visible en la ventana de diálogo. Al igual
que confirm, el método prompt devolverá un resultado que depende de la entrada del
usuario.

Si el usuario cierra la ventana con el botón Aceptar, todo lo que se encuentre en el campo de
texto se devolverá desde el método prompt como una cadena. Si el cuadro de diálogo se
cierra con el botón Cancelar, el método devolverá un valor null. Debido a que al pulsar el
botón Aceptar el valor devuelto será de tipo String, en ocasiones necesitaremos convertirlo a
otro tipo, por ejemplo, a un tipo Number. Al igual que con todas las entradas de los usuarios,
debemos estar preparados para el hecho de que los datos proporcionados por el usuario
pueden no ser válidos, ya sea por error o a propósito, por lo que siempre trata los valores
ingresados con precaución.

let name = window.prompt("¿Cuál es tu nombre?", "Juan Pérez");


name = name ? name : "anónimo";

let age = prompt("Hola " + name + " ¿Cuántos años tienes?");


alert(name + " tiene " + age + " años");

Resumen de Sección
Los cuadros de diálogo pueden no ser la forma más efectiva y elegante de comunicarse con el
programa, pero serán completamente suficientes para nuestras necesidades. Te permitirán
recuperar datos y tener en cuenta las decisiones del usuario, que podrán influir en el
programa.

Tareas

Tarea 1

Usando todo lo que has aprendido hasta este punto, escribe una secuencia de comandos que
le pregunte al usuario sobre el ancho, alto y largo de una caja, luego calcula y devuelve al
usuario el volumen de esta caja.

Como ejemplo, una caja con anchura = 20, altura = 10 y longitud = 50 tendrá un
volumen de 10000 (omitiendo unidades, ya que no son relevantes en este escenario). Por
ahora, supón que los valores proporcionados por el usuario son números válidos, pero si
tienes alguna idea sobre cómo hacerlo, puedes intentar hacer este script de tal manera que
sea resistente a los valores no válidos.

Ejemplo

let width = prompt("Volumen de la caja, ingresa el ancho", 0);

let height = prompt("Volumen de la caja, ingresa la altura", 0);

let length = prompt("Volumen de la caja, ingresa la longitud", 0);

let volume = width * height * length;

alert(`El volumen de la caja es ${volume}`);


Ejecución condicional
La ejecución condicional o las sentencias de control de flujo ya se han mencionado varias
veces y ahora es el momento de examinarlas con detalle. Este es un tema muy importante, ya
que las sentencias control de flujo son esenciales no solo para JavaScript, sino también para
la programación en general. Sin la capacidad de reaccionar y cambiar su comportamiento,
cualquier código siempre haría lo mismo. Por supuesto, esto es a veces exactamente lo que
necesitamos, pero la mayoría de las veces necesitamos capacidad de respuesta y la
capacidad de manejar diferentes situaciones en el código.

Podemos imaginar nuestro programa como un árbol que comienza desde el tronco y se divide
gradualmente en ramas. El tronco es el comienzo del programa, y cada instrucción condicional
es un reflejo de una nueva rama. Las instrucciones condicionales se ejecutan sobre la base de
la decisión del usuario, los resultados de cálculos anteriores u otra información que el
programa tendrá en cuenta. JavaScript proporciona algunas formas de bifurcar la ejecución
del código, basado en condiciones arbitrarias. A partir de este capítulo, habrá más tareas y
código que deberás escribir, ya que ahora deberías tener a mano casi todas las herramientas
necesarias.

La instrucción if

La instrucción if es la primera y más simple instrucción de control de flujo disponible en


JavaScript. Tiene algunas formas, pero en su forma básica, verifica una condición
determinada y, según su valor booleano, ejecuta un bloque de código o lo omite. La sintaxis
se ve así:

if (condición) {

bloque de código

La palabra clave if debe ir seguida de la expresión entre paréntesis, que se evaluará como
booleana, y si el resultado es verdadero, se ejecuta el bloque de código que sigue a la
expresión condicional. Si la expresión se evalúa como falsa, el bloque de código NO se
ejecutará. El bloque de código debe separarse mediante corchetes.

Comencemos con un ejemplo simple, en el que, además de la instrucción condicional,


utilizaremos los cuadros de diálogo aprendidos recientemente:

let isUserReady = confirm("¿Estás listo?");

console.log(isUserReady);

if (isUserReady) {

alert("¡Usuario listo!");
}

En el ejemplo, una alerta con el mensaje "¡Usuario listo!" se mostrará solo cuando el usuario
cierre el cuadro de diálogo de confirmación haciendo clic en el botón Aceptar. Pero si el
usuario cierra el cuadro de confirmación "¿Estás listo?" sin hacer clic en Aceptar, el mensaje
"¡Usuario listo!" el mensaje nunca se mostrará.

La instrucción if
En el ejemplo, hay una línea dentro del bloque de código if, lo que significa que podríamos
omitir las llaves alrededor del bloque. Sin embargo, aunque puede parecer tentador hacerlo,
siempre es mejor usar llaves. De esa forma, el código es más limpio y fácil de leer, y también
evita un error muy común que ocurre cuando se intenta agregar otra instrucción dentro de un
bloque if y se olvida agregar las llaves.

En el siguiente ejemplo, probablemente nos olvidamos de cerrar los dos comandos dentro del
bloque directamente detrás de la instrucción if. Comprueba cómo se comportará este
fragmento de código cuando lo ejecutes, según tu respuesta a la pregunta "¿Estás listo?":

let isUserReady = confirm("¿Estás listo?");

if (isUserReady)
console.log("¡Usuario listo!");
alert("¡Usuario listo!");

Corrige este código cerrando ambos comandos (console.log y alert) en el bloque. Comprueba
cómo afectará esto al programa.

Hemos hablado de bloques de código, y más concretamente de su alcance, en el apartado


dedicado a las variables. Si no recuerdas este tema, sería bueno que volvieras a él por un
tiempo. Como recordatorio rápido, usamos bloques de programa cada vez que, por alguna
razón, queremos separar una pieza de código para formar un todo lógico. Las instrucciones
condicionales son un buen ejemplo. Usando la instrucción if y cerrando las instrucciones
seleccionadas en el bloque (usando llaves), podemos hacer que se ejecuten solo cuando
ocurren ciertas circunstancias (es decir, se cumple la condición que hemos inventado).
Recuerda que las variables y constantes declaradas usando let y const dentro de un bloque
son locales, es decir, su alcance está limitado a ese bloque en particular. También asegúrate
de usar sangrías en los bloques para que el código sea más legible. En otras palabras, mueve
las instrucciones en ellos a la derecha.

Como ya hemos mencionado, mediante el uso de bloques de datos, podemos hacer que las
constantes y variables declaradas dentro de ellos sean locales (no visibles desde el exterior).
Hablamos de esto en detalle en la sección de variables, por lo que aquí solo presentaremos
un ejemplo práctico para recordarlo:

let unitPrice = 10;


let pieces = prompt("¿Cuántas piezas se ordenaron?", 0);
if (pieces > 0) {
let total = unitPrice * pieces;
console.log(total);
}
console.log(total); // -> Uncaught ReferenceError: total is not
defined

La variable total se declara dentro del bloque. Podemos ponerle un valor, podemos leerlo,
pero todo esto solo dentro del bloque. Intentar referirse a él fuera del bloque terminará en un
error. El intérprete nos informará que no conoce tal variable.

La instrucción if - continuación
Este es un buen momento para recordarte las operaciones lógicas y de comparación, ya que
se usan más comúnmente como condiciones, especialmente en situaciones más complejas.
Veamos el ejemplo:

let userAge = 23;


let isFemale = false;
let points = 703;
let cartValue = 299;
let shippingCost = 9.99;

if (userAge > 21) {


if (cartValue >= 300 || points >= 500) {
shippingCost = 0;
}
}

console.log(shippingCost);

En este ejemplo, para establecer la variable shippingCost a cero, la variable userAge


necesita ser mayor que 21. Para entrar al segundo if, la variable cartValue necesita ser
mayor o igual que 300, or o la variable points mayor o igual que 500.

Otra forma de escribir lo mismo es usar el operador lógico AND. Ahora podemos eliminar una
instrucción if y describir todo como una condición más compleja. Toma en cuenta que usamos
paréntesis adicionales para agrupar las operaciones lógicas seleccionadas. Esto nos permitirá
forzar su precedencia de ejecución.

if (userAge > 21 && (cartValue >= 300 || points >= 500)) {


shippingCost = 0;
}

Aquí la condición se evaluará como verdadera cuando:

 userAge sea mayor que 21 AND (Y)


 cartValue sea mayor o igual que 300 OR (O) points sea mayor o igual que 500.
Entonces, necesitamos que se cumpla la primera condición, y al menos una de las
condiciones segunda o tercera.

La instrucción if ... else


La instrucción if es muy útil, pero ¿qué pasa si también queremos ejecutar algún código
cuando no se cumple una condición dada? Por supuesto, podríamos usar otra declaración if y
cambiar la condición, como se muestra en el ejemplo:

let isUserReady = confirm("¿Estás listo?");

if (isUserReady) {
console.log("¡Usuario listo!");
}

if (isUserReady == false) {
console.log("¡Usuario no esta listo!");
}

Esto funcionará como se esperaba, pero no se ve muy bien. ¿Y si en el futuro tenemos que
cambiar esta condición? ¿Recordaremos cambiarlo en ambos lugares? Este es un posible
error lógico futuro. Entonces, ¿qué podemos hacer? Podemos usar una palabra clave else.

La palabra clave else es una parte opcional de la instrucción if y nos permite agregar un
segundo bloque de código que se ejecutará solo cuando NO se cumpla la condición inicial.
Ambos bloques de código se separan mediante la palabra clave else.

Entonces la sintaxis if ... else es como sigue:

if (condición) {
condición - código verdadero
} else {
condición - código falso
}

Usando la nueva sintaxis, podemos reescribir nuestro ejemplo


anterior:

let isUserReady = confirm("¿Estás listo?");

if (isUserReady) {
console.log("¡Usuario listo!");
} else {
console.log("¡Usuario no esta listo!");
}

Ahora solo tenemos una condición y estamos seguros de que se ejecutará uno de los dos
bloques de código. Esta estructura se usa con mucha frecuencia y es especialmente útil
cuando tenemos dos versiones alternativas del código.

La instrucción if ... else ... if


Las instrucciones if y if... else nos dan una gran flexibilidad en cómo se puede comportar el
código en relación con cualquier otra cosa. Pero ramificar el flujo de ejecución del código solo en dos
ramas a veces no es suficiente. Hay una solución simple para esto en JavaScript: podemos anidar
instrucciones if ... else. ¿Como funciona esto? Un segmento else puede tener una
sentencia if o if... else dentro de él, y es posible anidar cualquier número de sentencias if...
else si es necesario.

La sintaxis para esto se ve así:

if (condición_1) {
code
} else if (condición_2) {
code
} else if (condición_3) {
code
} else {
code
}

Veamos un ejemplo simple en el que usamos un if "de varios niveles":

let number = prompt("Ingresa un número", 0);

if (number < 10) {


alert("<10");
} else if (number < 30) {
alert("<30");
} else if (number < 60) {
alert("<60");
} else if (number < 90) {
alert("<90");
} else if (number < 100) {
alert("<100");
} else if (number == 100) {
alert("100")
} else {
alert(">100")
}

En el código visible en el ejemplo, solo se mostrará una alerta y JavaScript dejará de verificar las
condiciones después de que se haya cumplido la primera condición.

La instrucción if ... else ... if - continuación


En el siguiente ejemplo, podemos ver el uso de ifs en cascada con elses, y también
condiciones lógicas complejas. Siéntete libre de jugar con los valores asignados a las
variables para ver cómo cambian los resultados.

Para resumir lo que está pasando, podemos diseccionar cada caso por separado:

 Si userAge es mayor que 65 O


 Si userAge es mayor o igual que 21 Y uno de los siguientes casos:
- hasParentsApproval es verdadero.
- cartValue es mayor que 300.
- points es mayor que 500.
entonces shippingCost se reducirá a cero.
 Sino y si userAge es menor que 21 y hasParentsApproval es
verdadero, shippingCost se reducirá en 5.
 Si userAge es menor que 21 pero hasParentsApproval es falso, la orden es
inválida.

En cualquier otro caso, shippingCost se mantiene en su valor predeterminado.

Después de todo esto, hacemos otra verificación:

 Si isOrderValid es verdadero
 Y addInsurance es verdadero
 Y hasPromoCode es falso, entonces se le suma INSURANCE_COST a shippingCost.

Al final, mostramos el shippingCost si isOrderValid es verdadero y el mensaje "No se


puede ordenar si es menor que 21" si no lo es.

Tómate tu tiempo con este ejemplo y juega con los valores para entender qué está pasando y
cómo.

Operador condicional
Hemos hablado del operador condicional (ternario) en la parte del curso dedicada a los
operadores. Nos permite realizar una de dos acciones en función del valor del primer
operando. La mayoría de las veces se usa como una alternativa a la instrucción if ...
else cuando deseas asignar uno de dos valores a una variable, dependiendo de una
determinada condición. En tales casos, es simplemente más corto y un poco más legible que
la instrucción if... else. Veamos un ejemplo simple, sin usar un operador condicional:

let price = 100;


let shippingCost;
if (price > 50) {
shippingCost = 0;
} else {
shippingCost = 5;
}
console.log(`price = ${price}, shipping = ${shippingCost}`); // ->
price = 100, shipping = 0
El mismo código reescrito con el operador condicional parece un poco más sencillo. Como
recordatorio: el primer operando (antes del signo de interrogación) es la condición a verificar,
el segundo operando (entre el signo de interrogación y los dos puntos) se devolverá si la
condición es verdadera, y el tercer operando (después de los dos puntos) si la condición es
falsa.

let price = 100;


let shippingCost = price > 50 ? 0 : 5;

console.log(`price = ${price}, shipping = ${shippingCost}`); // ->


price = 100, shipping = 0

El operador condicional devuelve los valores del segundo o tercer operando, pero si son
expresiones complejas, los calculará primero. Puedes usar este hecho para colocar los
comandos que se ejecutarán condicionalmente como estos operandos.

let start = confirm("¿Iniciar?");


start ? alert("¡Aquí vamos!") : console.log("Abortado");

Sin embargo, tendría más sentido guardar el mismo fragmento de código de la siguiente
forma:

let start = confirm("¿Iniciar?");


let message = start ? "¡Aquí vamos!" : "Abortado";
alert(message);

Es posible tener código más largo como operandos, pero es mejor usar sentencias if, ya que
es mucho más claro.

La sentencia switch ... case


El último tipo de sentencia que se utiliza para la ejecución de código condicional es una
setencia switch. Estamos hablando de esto ahora porque, entre otras cosas, en comparación
con la instrucción if, no es una sentencia que se use con mucha frecuencia. Es algo similar a
las sentencias if... else anidadas, pero en lugar de evaluar diferentes expresiones, switch
evalúa una expresión condicional y luego intenta hacer coincidir su valor con uno de los casos
dados. Esta es la sintaxis de la sentencia switch:

switch (expresión) {
case primera_coincidencia:
código
break;
case segunda_coincidencia:
código
break;
default:
código
}
Comienza con la palabra clave reservada switch seguida de la expresión a evaluar entre
paréntesis. El siguiente es el bloque de código que tiene una o más cláusulas de caso
(técnicamente es posible tener cero casos, pero esto no sería muy útil) seguido directamente
por un valor correspondiente a este caso y un carácter de dos puntos. Después de los dos
puntos, hay un bloque de código que se ejecutará cuando la expresión se evalúe con este
valor de caso. El bloque de código termina opcionalmente con la palabra clave break. Todos
los casos siguen la misma plantilla.

Además, puede estar presente un caso especial llamado default (por convención ubicado al
final de la instrucción switch; sin embargo, no es obligatorio). El caso default se ejecuta
cuando ninguno de los casos coincide con la expresión. La evaluación en sí se realiza con un
operador de comparación estricto (===) por lo que no solo debe coincidir el valor, sino
también el tipo de valor de caso y la expresión.

Para entenderlo mejor, ejecuta el siguiente código:

let gate = prompt("Elegir la puerta: a, b, o c");


let win = false;

switch (gate) {
case "a":
alert("Puerta A: Vacía");
break;
case "b":
alert("Puerta B: Premio Mayor");
win = true;
break;
case "c":
alert("Puerta C: Vacía");
break;
default:
alert("No existe la Puerta " + String(gate));
}

if (win) {
alert("¡Ganador!");
}

Resumen
Las sentencias de control de flujo son una parte esencial de casi cualquier aplicación y las
usamos para verificar y manejar las entradas del usuario, los valores obtenidos de la web o los
resultados de casi cualquier operación. Nos permiten construir aplicaciones flexibles y
reactivas. La construcción más universal es la sentencia o instrucción if ... else.

Sin embargo, no olvides que en algunas situaciones será más conveniente usar un operador
condicional o una sentencia switch.
Tareas

Tarea 1

Escribe un script que le pida al usuario que ingrese un número. Muestra el


mensaje "¡Bingo!" cuando el número sea mayor que 90 pero menor que 110, de lo contrario
muestra el mensaje: "¡Fallaste!". Utiliza la sentencia if .

Ejemplo

let number = prompt("Introduce un número aleatorio: ");

if(number > 90 && number < 110) {

alert("¡Bingo!");

} else {

alert("¡Fallaste!");

tarea 2

Reescribe el código anterior, reemplazando el if con un operador condicional ternario.

Ejemplo

let number = prompt("Introduce un número aleatorio: ");

let message = (number > 90 && number < 110) ? "¡Bingo!": "¡Fallaste!";

alert(message);

Tarea 3

Escribe una aplicación de calculadora simple. Solicite al usuario la siguiente entrada, uno por
uno: dos números y un carácter que representa una operación matemática de "+", "-", " *", o
"<código style="box-sizing: border-box;">/</código>". Si la entrada del usuario es válida,
calcula el resultado y muéstralo al usuario. Si la entrada del usuario no es válida, muestra un
mensaje que informa al usuario que se ha producido un error. Recuerda que el valor devuelto
por la función prompt es del tipo cadena. Puedes usar el método Number.isNaN para verificar
si se obtiene el número correcto después de la conversión. Por ejemplo, llamar
a Number.isNaN(10) devolverá false, mientras que Number.isNaN(NaN) devolverá true.

Ejemplo

let firstNumber = Number(prompt("Introduce el primer número: "));


let secondNumber = Number(prompt("Introduce el segundo número: "));

let operand = prompt("Introduce el operando (+, -, * o /)");

let result;

if (!Number.isNaN(firstNumber) && !Number.isNaN(secondNumber)) {

switch (operand) {

case "+": result = firstNumber + secondNumber; break;

case "-": result = firstNumber - secondNumber; break;

case "*": result = firstNumber * secondNumber; break;

case "/": result = firstNumber / secondNumber; break;

default: result = "Error: operando desconocido";

} else {

result = "Error: al menos uno de los valores ingresados no es un


número";

alert(result);

¿Qué son los bucles?


En la programación, los bucles son la segunda forma de las sentencias de control de flujo. Junto con las
sentencias de ejecución condicional como el if y switch, permiten una libertad casi infinita sobre cómo
puede funcionar la aplicación desde un punto de vista algorítmico. Si bien las sentencias condicionales
pueden cambiar el comportamiento del código (permitiéndonos decidir durante la ejecución del
programa si se debe ejecutar o no una determinada parte del código), los bucles son una manera fácil de
repetir cualquier fragmento del código tantas veces como queramos, o hasta que se cumpla alguna
condición. Existen diferentes tipos de bucles en JavaScript, pero podemos dividirlos en dos categorías:

 Bucles que se repiten un número determinado de veces.


 Bucles que continúan hasta que se cumple alguna condición.

El bucle while

Así que sabemos que los bucles nos permiten ejecutar un fragmento de código seleccionado varias
veces. Pero, ¿cuál sería el propósito de esto? Imagina que en un programa hemos creado un arreglo que
contiene información sobre los usuarios del sistema. Si quisiéramos mostrar el nombre de cada uno de
ellos en la consola, tendríamos que escribir console.log tantas veces como usuarios. Por ejemplo, para
100 usuarios escribimos 100 llamadas de console.log una debajo de otra. Se vería bastante raro, ¿no? En
su lugar, podemos usar un bucle que llame al mismo comando console.log 100 veces, pero para cada
elemento subsiguiente del arreglo. Cada iteración del bucle se referirá al usuario sucesivo. En un
momento, aprenderemos a usar diferentes bucles.

Comencemos con un ejemplo en el que no usamos un bucle. Nuestro objetivo es escribir los números
consecutivos 0, 10, 20, 30, 40, 50, 60, 70, 80 y 90 en la consola.

console.log(0); // -> 0

console.log(10); // -> 10

console.log(20); // -> 20

console.log(30); // -> 30

console.log(40); // -> 40

console.log(50); // -> 50

console.log(60); // -> 60

console.log(70); // -> 70

console.log(80); // -> 80

console.log(90); // -> 90

Con todo, hemos logrado nuestro efecto, pero se ve un poco extraño. Repetimos el mismo comando diez
veces, uno por uno. A primera vista, puede parecer que los comandos no son exactamente iguales. Sí,
cada vez que llamamos a console.log aparece un literal diferente como argumento: 0, 10, 20, etc.
Vamos a modificar el código para mostrar que es exactamente la misma acción.

let n = 0;

console.log(n); // -> 0

n += 10;

console.log(n); // -> 10

n += 10;

console.log(n); // -> 20

n += 10;

console.log(n); // -> 30

n += 10;

console.log(n); // -> 40

n += 10;

console.log(n); // -> 50

n += 10;

console.log(n); // -> 60

n += 10;

console.log(n); // -> 70

n += 10;

console.log(n); // -> 80

n += 10;

console.log(n); // -> 90

n += 10;
Usamos la variable n, que inicializamos con 0. Luego repetimos este fragmento de código idéntico diez
veces: mostramos el valor actual de la variable n en la consola y luego aumentamos este valor en 10.

console.log(n);

n += 10;

Esta es exactamente la situación en la que podemos usar un bucle. Usando el bucle while, reescribimos
nuestro código nuevamente. Dejamos la sentencia e inicialización de la variable n sin cambios. El
fragmento de código repetitivo se incluye en un bloque de código separado y, al usar la palabra while,
especificamos que debe ejecutarse siempre que el valor de la variable n sea inferior a 91.

let n = 0;

while(n < 91) {

console.log(n); // -> 0, 10, 20, 30, 40, 50, 60, 70, 80, 90

n += 10;

El bucle while - continuación


El bucle while es tan versátil que alguien lo suficientemente persistente podría reemplazar
todas las demás sentencias de control de flujo con bucles while, incluso sentencias if. Por
supuesto, sería engorroso en el mejor de los casos. El bucle while es uno de los bucles que
normalmente usamos cuando no sabemos exactamente cuántas veces se debe repetir algo,
pero sabemos cuándo parar. La sintaxis del bucle while es la siguiente:

while(condición) {
bloque de código
}

La expresión entre paréntesis se evalúa al comienzo de cada iteración del bucle. Si la


condición se evalúa como verdadera, se ejecutará el código entre llaves. Luego, la ejecución
vuelve al comienzo del bucle y la condición se evalúa nuevamente. El bucle iterará y el código
se ejecutará siempre que la condición se evalúe como verdadera. Esto significa, por supuesto,
que con una condición mal formada, un bucle while puede convertirse en un bucle infinito, un
bucle sin final. Dependiendo del contexto, eso puede ser lo que queremos lograr. Casi todos
los juegos de computadora, por ejemplo, son básicamente bucles infinitos que se repiten: leer
la entrada del jugador, actualizar el estado del juego, mostrar en la pantalla tantas veces por
segundo como sea necesario. Esta es una gran simplificación, pero no obstante es cierta.
Veamos un ejemplo más. Esta vez, la decisión de si el bucle debe finalizar será tomada por el
usuario respondiendo a la pregunta formulada durante cada iteración del bucle (usaremos el
cuadro de diálogo confirm que presentamos recientemente).

let isOver = false;


let counter = 1;

while (isOver != true) {


let continueLoop = confirm(`[${counter}] ¿Continuar en el bucle?
`);
isOver = continueLoop === true ? false : true;
counter = counter + 1;
}

El bucle se repetirá hasta que la variable isOver se establezca en verdadero. En el bucle,


mostramos la pregunta: "¿Continuar en el bucle?", que está precedida por el número de
iteración (la variable counter). Toma en cuenta que la variable counter no se usa en la
condición while, ya que su función es solo informativa. Al hacer clic en el botón Aceptar (OK)
en el cuadro de diálogo de confirmación, la variable continueLoop se establecerá en
verdadero (de lo contrario, se establecerá en falso). Según el valor de la
variable continueLoop, establecemos un nuevo valor para la variable isOver. Recuerda que
la variable que se prueba en la condición while debe inicializarse previamente. Este es uno de
los errores más comunes de los programadores principiantes: recuerdan cambiar su valor
dentro del bucle, pero se olvidan de establecer su valor para la primera prueba.

El ejemplo es correcto, pero aquí hay mucho código redundante, que no es realmente
necesario (solo lo hicimos en primer lugar para poder analizar el funcionamiento de las
instrucciones subsiguientes de una manera relativamente simple). Se logrará exactamente el
mismo efecto simplificando nuestro código como se muestra a continuación. Trata de analizar
exactamente de qué se tratan los cambios. Hemos utilizado para este propósito, entre otros,
los operadores que aprendimos recientemente.

let isOver = false;


let counter = 1;

while (!isOver) {
isOver = !confirm(`[${counter++}] ¿Continuar en el bucle?`);
}

El bucle do ... while


El bucle do ... while es muy similar al bucle simple while, la principal diferencia es que en
un bucle while, la condición se verifica antes de cada iteración , y en el bucle do ... while,
la condición se verifica después de cada iteración. Esto no parece una gran diferencia, pero la
consecuencia de esto es que un código de bucle do ... while siempre se ejecuta al menos
una vez antes de que se realice la primera verificación de condición, y un < codel>while nunca
se puede ejecutar si la condición inicial se evalúa como falsa al comienzo del bucle. La
sintaxis del bucle do ... while es la siguiente:

do {
bloque de código
} while(condición);

Reescribamos nuestro último ejemplo usando do ... while en lugar de while. Nota que
esta vez la variable isOver no necesita inicializarse antes del bucle (la condición se verifica al
final del bucle y se llamará al cuadro de diálogo de confirmación antes de la primera prueba).

let isOver;
let counter = 1;

do {
isOver = !confirm(`[${counter++}] ¿Continuar en el bucle?`);
} while (!isOver);

El comportamiento del programa será idéntico al anterior, en el que usábamos while. Compara
ambos ejemplos, y deberías ver fácilmente las diferencias causadas por el uso de diferentes
sentencias de bucle.

En el siguiente ejemplo, demostramos lo que dijimos antes - un do ... while loop siempre itera
al menos una vez:

let condition = false;

while (condition) {
console.log("Una iteración del bucle while."); // nunca se
ejecuta
}

do {
console.log("Una iteración del bucle do ... while."); //
ejecutado una vez
} while (condition);

El bucle for
El bucle for forma parte del segundo tipo de bucles, el que es mejor en situaciones en las que sabemos
cuántas veces se debe ejecutar el bucle (aunque esto no es necesario). La sintaxis del bucle for es un
poco más complicada y tiene el siguiente aspecto:

for (inicialización; condición; incremento) {

bloque de código

}
En los paréntesis después de la palabra for, esta vez no encontraremos una sola condición, como sucedió
en el bucle while. El interior de los paréntesis se divide en tres campos por punto y coma, y a cada
campo se le asigna un significado diferente. En cada uno de estos campos aparece una sentencia, que se
interpretará de la siguiente manera:

 Inicialización del bucle.


 Condición del bucle.
 Incremento del bucle.

Las tres sentencias son opcionales, pero de hecho rara vez se nos escapa alguna. Echemos un vistazo
más de cerca a ellos.

La inicialización del bucle for

La inicialización se ejecuta solo una vez, antes de la primera iteración del bucle. Por lo general, se usa
para inicializar (o declarar e inicializar) una variable que se usará como contador de bucle. Podemos usar
cualquier variable existente como contador, pero en general es una buena práctica declarar una nueva, ya
que esto hace que el código sea más limpio y más fácil de leer y entender. La declaración de la
inicialización es opcional y se puede dejar vacía, excepto si termina con un punto y coma.

La condición del bucle for

Una condición es una expresión que se evalúa como un valor booleano antes de cada iteración del bucle.
Si esta expresión se evalúa como verdadera, el bucle ejecutará su código. En el caso de que la condición
se evalúe como falsa, el bucle finaliza y no se ejecutarán más iteraciones, y la ejecución del código salta
a la primera sentencia después del bucle for. La condición también es opcional, pero si se deja vacía, se
asumirá que siempre es verdadera, lo que generará un bucle infinito si no se utiliza ningún otro medio
para salir del bucle.

El incremento del bucle for

El incremento siempre se ejecuta al final de la iteración del bucle actual y, la mayoría de las veces, se
usa para incrementar (o disminuir, según la necesidad) un contador que se usa en una condición. Puede
ser cualquier expresión, no solo incremento/decremento. Esto también se puede dejar vacío.

Puede parecer un poco complicado, pero después del primer ejemplo simple, todo debería quedar más
claro. Usando el bucle for, intentaremos escribir enteros sucesivos del 0 al 9 en la consola, por lo que
haremos diez iteraciones:

for (let i = 0; i < 10; i++) {

console.log(i);

}
Como se muestra en la sintaxis del bucle for, hay tres expresiones dentro de los paréntesis. El let i = 0 es
una inicialización, i < 10 es una condición e i++ es un incremento. Ahora intentemos reescribir el mismo
ejemplo usando el bucle while:

let i = 0;

while (i < 10) {

console.log(i);

i++;

l bucle for - continuación


En ambos casos (bucle for y bucle while), declaramos e iniciamos la variable i antes de que comience el
bucle (establecida inicialmente en 0). Ambos bucles se ejecutarán siempre que la condición i < 10 se
cumpla. En ambos bucles, al final de cada iteración, el valor de la variable i se incrementará en 1. Y por
supuesto, en ambos bucles de cada iteración, el valor actual de la variable i se imprimirá en la consola.
Entonces puedes ver que el ciclo for en realidad ofrece una forma ligeramente diferente y más
consistente de escribir lo mismo que se puede lograr con el ciclo while. Tal notación es particularmente
útil en casos como el presentado en el ejemplo, donde usamos un contador de iteraciones (en nuestro
ejemplo, la variable i), que debe ser inicializada e incrementada (o decrementada). En tal situación, todo
lo relacionado con el contador (inicialización, condición de finalización del bucle, cambio de valor del
contador) se encuentra prácticamente en un solo lugar, lo que puede aumentar la legibilidad del código.

Un ejemplo típico del uso de un bucle for, en el que sabemos el número de iteraciones por adelantado, es
cuando se revisan los elementos de un arreglo. Pasemos a otro ejemplo sencillo. Supongamos que
tenemos un arreglo de números enteros de cuatro elementos a nuestra disposición y nuestro sueño es
sumar todos estos números. No hay nada más fácil que recorrer el arreglo, tomando los elementos uno
por uno y agregándolos al resultado. Solo tenemos que recordar que antes de iniciar el bucle, el resultado
es 0.

let values = [10, 30, 50, 100];


let sum = 0;
for (let i = 0; i < 4; i++) {
sum += values[i];
}
console.log(sum); // -> 190

Sencillo, ¿verdad? Pero hay una ligera incomodidad en el código. Hemos establecido el número de
iteraciones a cuatro. Es cierto que hay exactamente cuatro elementos en el arreglo, y en nuestro ejemplo
este número no cambia, pero no es una regla universal. En los programas normales, los arreglos suelen
cambiar dinámicamente durante la ejecución del programa (se agregan o eliminan elementos). En este
caso, definitivamente es mejor usar la propiedad de los arreglos llamada length (la mencionamos cuando
discutimos sobre los arreglos). Esta propiedad contiene el número actual de elementos del arreglo. Así
que nuestro ejemplo reescrito correctamente se verá así:

let values = [10, 30, 50, 100];


let sum = 0;
for (let i = 0; i < values.length; i++) {
sum += values[i];
}
console.log(sum); // -> 190

Bucles y arreglos
Intentemos jugar de nuevo con los arreglos. Esta vez el programa será un poco más complicado.
Queremos que el usuario ingrese nombres durante la ejecución del programa (usaremos el cuadro de
diálogo prompt), que se colocarán en el arreglo uno por uno. La introducción de nombres finalizará
cuando se pulse el botón Cancelar. Luego, escribiremos todo el contenido del arreglo (es decir, todos los
nombres ingresados) en la consola.

let names = [];


let isOver = false;
while (!isOver) {
let name = prompt("Ingresa otro nombre o presiona cancelar.");
if (name != null) {
names.push(name);
} else {
isOver = true;
}
}

for (let i = 0; i < names.length; i++){


console.log(names[i]);
}

El arreglo names está inicialmente vacío. En cada iteración del bucle while llamamos al cuadro de
diálogo prompt. Si el usuario ingresa un nuevo nombre y presiona el botón OK, este nombre se
ingresará en la variable local name. Si el usuario hace clic en Cancelar, la variable contendrá un null.
Entonces verificamos en la instrucción condicional si el valor es diferente de null. Si es así, el valor de
la variable name se adjunta al final del arreglo names utilizando el método push (si no lo recuerdas,
vuelve al capítulo donde hablamos sobre arreglos). Si el valor es null, el valor de la
variable isOver se cambia para finalizar el bucle while.

Después de dejar el bucle while, pasamos por el arreglo (usando el bucle for y la propiedad length)
y mostramos todos los nombres dentro del arreglo. De esta manera, hemos hecho algo absolutamente
nuevo. Utilizando arreglos, bucles e interacción con el usuario (cuadro de diálogo prompt) hemos
creado y llenado una estructura de datos dinámica. Toma en cuenta que mientras escribes este programa,
no está claro cuántos elementos habrá en el arreglo, o incluso qué elementos habrá.

Revisando los elementos del arreglo, usamos una variable index que inicializamos con el valor de 0 (el
índice del primer elemento del arreglo) y la incrementamos de uno en uno durante cada iteración.
Obviamente, este no es el único enfoque. Por ejemplo, si quisiéramos recorrer los elementos del arreglo
en orden inverso, inicializaríamos la variable de índice con el valor del índice más grande y lo
disminuiríamos de uno en uno durante cada iteración. Tampoco hay nada que nos impida saltar algunos
elementos a la vez, incrementando o decrementando la variable índice en un valor mayor que uno. Echa
un vistazo al siguiente ejemplo:

let values = [10, 30, 50, 100];

for (let i = 0; i < values.length; i++) {


console.log(values[i]); // -> 10, 30, 50, 100
}

for (let i = values.length - 1; i > 0; i--) {


console.log(values[i]); // -> 100, 50, 30, 10
}

for (let i = 0; i < values.length; i += 2) {


console.log(values[i]); // -> 10, 50
}

for ... of
Además del bucle for regular, hay dos versiones específicas, una de las cuales, for... of, está
dedicada para usar con arreglos (y otras estructuras iterativas, que sin embargo están más
allá del alcance de este curso). En un bucle de este tipo, no especificamos explícitamente
ninguna condición ni número de iteraciones, ya que se realiza exactamente tantas veces como
elementos haya en el arreglo indicado.

Volvamos a nuestro ejemplo de sumar los números del arreglo de cuatro elementos. En este
ejemplo, usamos un bucle for simple:

let values = [10, 30, 50, 100];


let sum = 0;
for (let i = 0; i < values.length; i++) {
sum += values[i];
}
console.log(sum); // -> 190

La versión de este programa que usa el bucle for ... of se verá ligeramente diferente:

let values = [10, 30, 50, 100];


let sum = 0;
for (let number of values) {
sum += number;
}
console.log(sum); // -> 190

Entre corchetes después de la palabra for, no encontrarás tres campos separados por punto y
coma. Hay una declaración de variable, seguida de la palabra of y luego un arreglo, cuyos
elementos recorreremos (variable o literal). En nuestro ejemplo, for (let number of values)
significa que la variable number contendrá los elementos subsiguientes del arreglo values en
cada iteración. La sintaxis de este bucle es la siguiente:

for (variable of arreglo) {


bloque de código
}

En la práctica, con mucha más frecuencia que las diferentes versiones del bucle for, el método
forEach se usa para iterar a través de los elementos de un arreglo. Este es uno de los
métodos array type. Su uso requiere la capacidad para escribir tus propias funciones, por lo
que volveremos a él en la sección de funciones del curso.

Veamos un ejemplo más. Esta vez, declaramos un arreglo de ciudades, que contiene objetos
que describen algunas de las ciudades más grandes del mundo. Cada objeto contiene los
campos de nombre y población. Usando el bucle for... of, recorremos este arreglo y
mostramos información sobre todas las ciudades que tienen más de 20 millones de
habitantes.

let cities = [
{ name: "New York", population: 18.65e6 },
{ name: "Cairo", population: 18.82e6 },
{ name: "Mumbai", population: 19.32e6 },
{ name: "São Paulo", population: 20.88e6 },
{ name: "Mexico City", population: 21.34e6 },
{ name: "Shanghai", population: 23.48e6 },
{ name: "Delhi", population: 25.87e6 },
{ name: "Tokyo", population: 37.26e6 }
];

for (let city of cities) {


if (city.population > 20e6) {
console.log(`${city.name} (${city.population})`);
}
}

Ejecuta el código y luego experimente con las condiciones (por ejemplo, muestra todas las
ciudades con más de 20 millones de habitantes pero menos de 25 millones, etc.).

for ... in
También existe una versión del bucle for que nos permite recorrer campos de objetos. La
sintaxis es for ... in. Itera a través de todos los campos del objeto indicado, colocando los
nombres de estos campos (o claves) en la variable. En el ejemplo, usamos un objeto que
contiene datos sobre un usuario:

let user = {
name: "Calvin",
surname: "Hart",
age: 66,
email: "[email protected]"
};

for (let key in user) {


console.log(key); // -> name, surname, age, email
};

En cada iteración del bucle, los nombres de los campos sucesivos del objeto de usuario se
asignan a la variable key, es decir: nombre, apellido, edad, correo electrónico. Luego los
escribimos en la consola. Pero, ¿y si queremos recuperar los valores almacenados en los
campos con estos nombres? Para obtener acceso al campo especificado, usamos la notación
de puntos (la parte del curso dedicada a los tipos de datos), es decir, después del nombre del
objeto, escribimos un punto y el nombre del campo (key). La clave dada en esta notación
siempre se trata como un literal. En el bucle for... in, este enfoque no funcionará porque el
nombre del campo (key) se coloca en una variable. Afortunadamente, tenemos una solución
alternativa, la notación entre paréntesis. Te permite referirte al campo del objeto seleccionado
usando corchetes (como en los arreglos). En el corchete detrás del nombre del objeto,
colocamos el nombre del campo, que puede ser un literal o una variable que contenga ese
nombre.

console.log(user.name); // -> Calvin


console.log(user[name]); // -> Calvin

Usando la notación de corchetes, mejoramos nuestro ejemplo al mostrar no solo las claves,
sino también sus valores asignados.

for (let key in user) {


console.log(`${key} -> ${user[key]}`);
};

Las instrucciones break y continue


La instrucción break se utiliza para terminar la ejecución de un bucle o un switch. En cada
uno de estos contextos, cada vez que el motor de JavaScript encuentra una
instrucción break, sale de todo el bucle o el switch y salta a la primera instrucción después de
ese bucle o switch.

En el ejemplo, podemos ver un bucle infinito while del que se saldrá usando break después
de que la variable i sea mayor o igual a 5. Tal uso de break es solo de importancia ilustrativa,
porque tendría más sentido incluir esta condición directamente en la construcción del while.

let i = 0;
// Un bucle infinito
while (true){
console.log(i);
i++;
if (i >= 5) {
break;
}
}

alert(`Se salio del bucle con un break (${i}).`);

Al igual que break, continue se puede usar en bucles (pero no en un switch). Cuando se usa,
se aplica al bucle circundante más cercano. La instrucción continue, a diferencia de break, no
finaliza todo el bucle, sino que inicia la siguiente iteración de este bucle. Podemos pensar en
ello como saltar directamente al final de la iteración actual.

for (let i = 0; i < 10; i++) {


if (i == 3) {
continue;
}
console.log(i);
}

El programa escribe números del 0 al 9 en la consola, pero salta el número 3. Esto sucede
porque cuando i es igual a 3, se ejecuta la instrucción continue, finalizando esta iteración (y
omitiendo el console.log) y se comienza la siguiente iteración.

Tanto break como continue no se usan con mucha frecuencia. Definitivamente no deberíamos
usarlos cuando podemos terminar el bucle con una condición construida correctamente. Son
útiles en situaciones de emergencia, cuando sucede algo inusual en bucles con iteraciones
más complejas.

La palabra clave break


También necesitamos decir algunas palabras más sobre la palabra clave break. En el
ejemplo, la palabra clave break está presente en todos los casos excepto en el
caso default. A diferencia de las sentencias if, las sentencias switch no ejecutan solo una
rama, sino que ejecutan el código completo desde el primer caso que coincide hasta el final de
la sentencia switch. Este comportamiento se llama pasar a través de y tiene algunos usos,
pero la mayoría de las veces queremos ejecutar solo una rama, y por eso está presente la
palabra clave break. Cuando un intérprete de JavaScript llega a un break, saltará fuera de la
sentencia switch actual.

Para entender esto mejor, observa este ejemplo ligeramente modificado de un switch:

let gate = prompt("Elige una puerta: a, b, o c");


let win = false;

switch (gate) {
case "a":
alert("Puerta A: Vacía");
case "b":
alert("Puerta B: Premio Mayor");
win = true;
case "c":
alert("Puerta C: Vacía");
default:
alert("No existe la puerta " + String(gate));
}

if (win) {
alert("¡Ganador!");
}

La única diferencia es que ahora no hay palabras clave break. Ejecuta este código y verifica
qué sucede cuando se da la respuesta "a" al cuadro de diálogo. Ahora se muestran todas las
alertas, incluso la predeterminada.

La palabra clave break - continuación


El break puede ser útil cuando más de un caso debe terminar exactamente con el mismo
comportamiento.

let gate = prompt("Elige una puerta: a, b, o c");


let win = false;

switch (gate) {
case "a":
case "A":
case 1:
case "1":
alert("Puerta A: Vacía");
break;
case "b":
case "B":
case 2:
case "2":
alert("Puerta B: Premio Mayor");
win = true;
break;
case "c":
case "C":
case 3:
case "3":
alert("Puerta C: Vacía");
break;
default:
alert("No existe la puerta " + String(gate));
}

if (win) {
alert("¡Ganador!");
}

El código visible en el ejemplo se comportará igual cuando se proporcione "a", "A", 1 o "1"
como respuesta al cuadro de dialogo.
La palabra clave break - continuación
La última parte importante es que si se necesita un código más complejo dentro de un caso
determinado, debemos colocar ese código en bloques de código separados rodeándolo
adicionalmente con llaves. Esto aumentará la legibilidad del código y permitirá la declaración
de variables solo en el alcance del caso dado.

let gate = prompt("Elige una puerta: a, b, o c");


let win = false;

switch (gate) {
case "a": {
let message = "Puerta A";
console.log(message);
break;
}
case "b": {
let message = "Puerta B";
console.log(message);
break;
}
case "c": {
let message = "Puerta C";
console.log(message);
break;
}
default:
alert("No existe la puerta " + String(gate));
}

if (win) {
alert("¡Ganador!");
}

En el ejemplo, se observaría un error de redeclaración en cada uno de los casos y no se


estaría encapsulado en el propio alcance de cada caso.

Resumen
Sin el uso de un bucle, es difícil imaginar la escritura de programas. Su uso no solo facilita el
trabajo, sino que a menudo permite crear soluciones que no estarían disponibles sin ellos.
JavaScript proporciona muchos mecanismos que permiten repetir ciertas acciones en bucles,
recorrer elementos de arreglos, iterar a través de campos de objetos, etc. Hemos discutido
solo los más básicos, pero el while, o diferentes versiones del for, deberían ser suficiente para
crear programas bastante avanzados.

Tareas
Tarea 1

Escribe un fragmento de código que escriba números del 100 al 0 en la consola, pero en
pasos de 10 en 10; entonces sería 100, 90, 80... etc.

Ejemplo

for (i=100; i>=0; i-=10) {

console.log(i);

Tarea 2

Modifica el programa anterior para que le pida al usuario el primer y último número a usar en
lugar de 100 y 0 (pista: use el cuadro de diálogo prompt). Comprueba si los valores
introducidos son correctos (que el valor inicial sea mayor que el valor final).

Ejemplo

let upperLimit = Number(prompt("Ingresa limite superior"));

let lowerLimit = Number(prompt("Ingresa limite inferior"));

if (!Number.isNaN(upperLimit) && !Number.isNaN(lowerLimit) &&


upperLimit > lowerLimit) {

for (i = upperLimit; i >= lowerLimit; i -= 10) {

console.log(i);

Tarea 3

Hay diez números diferentes en este arreglo:


let numbers = [21, 45, 100, 12, 11, 78, 61, 4, 39, 22];

Escribe un programa que primero escriba todos estos números en la consola, luego solo los
que son pares (pista: el residuo de dividir un número par entre 2 es igual a 0), luego solo los
que son mayores que 10 y al mismo tiempo menor que 60.

Ejemplo

let numbers = [21, 45, 100, 12, 11, 78, 61, 4, 39, 22];

for (number of numbers) {

console.log(number);

for (number of numbers) {

if (number % 2 === 0) {

console.log(number);

for (number of numbers) {

if (number > 10 && number < 60) {

console.log(number);

Tarea 4

Escribe un programa utilizando un bucle que le pida al usuario el nombre de una película
(primer cuadro de dialogo) y su calificación de www.imdb.com (segundo cuadro de dialogo). El
programa te permitirá ingresar tantas películas como desees en el arreglo de películas. Cada
elemento del arreglo será un objeto, que constará de dos campos: título e imdb. La entrada se
completa si el usuario presiona Cancelar en el cuadro de diálogo. Luego, el programa debe
imprimir primero en la consola todas las películas que tienen una calificación inferior a 7, luego
aquellas cuya calificación sea mayor o igual a 7. Escribe el nombre de la película y su
calificación uno al lado del otro, por ejemplo:< /p>
Perdido en la Selva (7.7)
Ejemplo

let movies = [];

while (true) {

let title = prompt("Ingresa el título de la película");

let rating = prompt("Ingresa la calificación de la película


(imdb)");

if (title === null || rating === null) {

break

} else {

movies.push({

title: title,

rating: Number(rating)

});

console.log("Películas con calificaciones inferiores a 7:");

for (movie of movies) {

if (movie.rating < 7) {

console.log(`${movie.title} (${movie.rating})`);

}
console.log("Películas con calificaciones superiores a 7:");

for (movie of movies) {

if (movie.rating >= 7) {

console.log(`${movie.title} (${movie.rating})`);

break;

Tarea 5

El contenido del objeto que describe la posición del barco llamado "Mareno" se muestra en la
consola.

LATITUD -> 40.07288


LONGITUD -> 154.48535
CURSO -> 285.6
VELOCIDAD -> 14.0
OMI -> 9175717
NOMBRE -> MARENO

El código que se presenta a continuación se usa para esto. Completa el programa declarando
el objeto que falta en lugar de los tres puntos.

let vessel = ...

for( let key in vessel) {


console.log(`${key} -> ${vessel[key]}`);
}
Ejemplo

let vessel = {

LATITUD: 40.07288,

LONGITUD: 154.48535,

CURSO: 285.6,

VELOCIDAD: 14.0,

OMI: 9175717,
NOMBRE: "MARENO"

for (let key in vessel) {

console.log(`${key} -> ${vessel[key]}`);

Tarea 6

Modifica el programa de calculadora que creaste en el Módulo 4, Sección 2, de tal manera que
funcione en el bucle hasta que el usuario ingrese S en cualquiera de los cuadros de dialogo.

Ejemplo

while (true) {

let firstNumber = prompt("Introduce el primer número");

let secondNumber = prompt("Introduce el primer número");

let operand = prompt("Introduce el operando (+, -, * o /)");

let result;

if (firstNumber === "S" || secondNumber === "S" || operand ===


"S") {

break;

firstNumber = Number(firstNumber);

secondNumber = Number(secondNumber);
if (!Number.isNaN(firstNumber) && !Number.isNaN(secondNumber)) {

switch (operand) {

case "+":

result = firstNumber + secondNumber;

break;

case "-":

result = firstNumber - secondNumber;

break;

case "*":

result = firstNumber * secondNumber;

break;

case "/":

result = firstNumber / secondNumber;

break;

default:

result = "Error: operando desconocido";

} else {

result = "Error:al menos uno de los valores introducidos no es


un número";

alert(result);

Objetivos
Familiarizar al estudiante con:

 Bucles (qué son los bucles, el bucle while, el bucle do-while, el bucle for, el bucle for-
of, el bucle for-in, las instrucciones break y continue)
Escenario
Podemos mejorar un poco nuestro programa de lista de contactos mediante el uso de bucles.
Ahora puedes intentar mostrar no solo el primer o último contacto, sino todos los contactos de
la lista, independientemente de su número.

Además, intenta encerrar todo el programa en un bucle para que al usuario se le pregunte
repetidamente qué quiere hacer. El usuario puede optar por:

 Mostrar el primer contacto (primero)


 Mostrar el último contacto (último)
 Mostrar todos los contactos (todos)
 Añadir un nuevo contacto (nuevo)
 Salir del programa (salir)

Después de ejecutar la acción seleccionada, el programa le dará la oportunidad de elegir


nuevamente. El programa debe finalizar las acciones solo después de que el usuario dé un
comando específico, por ejemplo salir .

let contacts = [{

name: "Maxwell Wright",

phone: "(0191) 719 6495",

email: "[email protected]"

}, {

name: "Raja Villarreal",

phone: "0866 398 2895",

email: "[email protected]"

}, {

name: "Helen Richards",

phone: "0800 1111",

email: "[email protected]"

}];

Funciones
Hablamos de funciones en el capítulo sobre variables, cuando discutimos el alcance de la visibilidad de
las variables locales declaradas con la palabra clave var. Aprendimos en esa ocasión qué son las
funciones, pero en este capítulo las veremos mucho más de cerca, ampliando nuestro conocimiento sobre
el tema.
Entonces, ¿qué es una función? Es una pieza de código separada, que constituye un cierto todo lógico
cerrado, destinado a realizar una tarea específica. Por lo general, asignamos un nombre a esta pieza de
código separada. Con este nombre podemos llamarlo (ejecutarlo) muchas veces en diferentes lugares del
programa.

Esto es una simplificación, porque hay funciones que no tienen nombre, por ejemplo, funciones
anónimas (hablaremos de ellas más adelante). Por el momento, supongamos que una función tiene un
nombre, que le damos al declararla. Este nombre se utiliza cuando se llama o invoca la función, es decir,
cuando se ejecuta el código contenido en ella.

La declaración e invocación de funciones son independientes entre sí, lo que veremos en un momento.

¿Por qué usamos funciones?

Existen muchas razones, una de las más importantes es dividir el código en algunas partes lógicamente
independientes. Tal modularidad aumenta la legibilidad del código: es más fácil escribir y analizar un
programa que no es una secuencia de instrucciones individuales. También permite probar fácilmente
fragmentos de código cerrados en funciones independientemente del resto del programa.

Una razón muy importante para usar una función es la reutilización del código: si repites la misma
secuencia de instrucciones en el programa en muchos lugares, puedes colocar esta secuencia en una
función, y en esos lugares solo tiene que invocar a la función.

Además de reducir la cantidad de código en un programa (aumentando así su legibilidad), también


significa que si necesitas realizar algunos cambios en esta secuencia de instrucciones, debes hacerlo solo
una vez, dentro de la función. Si no usáramos una función en esta situación, tendríamos que hacer
cambios de forma independiente en cada lugar donde apareciera esta secuencia de instrucciones en el
código.

Echemos un vistazo a un programa simple, escrito sin ninguna función.

El programa calculará y mostrará la temperatura media diaria sobre la base de los datos
proporcionada (24 mediciones de temperatura, en intervalos de una hora, a partir de la
medianoche). En el programa declaramos la variable temperatures, que será una tabla de 24
elementos con las medidas obtenidas.

Tenemos medidas para dos días consecutivos, para lo cual haremos cálculos. La temperatura
media, por supuesto, se calcula sumando todos los valores y dividiendo el resultado entre la
cantidad.

A primera vista, puedes ver que el fragmento de código responsable de un cálculo se repite
dos veces. En dos lugares del programa, usamos la misma secuencia de instrucciones, por lo
que valdría la pena pensar en crear una función a partir de ellas.

Lo haremos en varias etapas, introduciendo algunos conceptos nuevos relacionados con las
funciones. Comencemos con una declaración de función.

let temperatures;
let sum;

let meanTemp;

temperatures = [12, 12, 11, 11, 10, 9, 9, 10, 12, 13, 15, 18, 21, 24,
24, 23, 25, 25, 23, 21, 20, 19, 17, 16];

sum = 0;

for (let i = 0; i < temperatures.length; i++) {

sum += temperatures[i];

meanTemp = sum / temperatures.length;

console.log(`mean: ${meanTemp}`); // -> media: 16.666666666666668

temperatures = [17, 16, 14, 12, 10, 10, 10, 11, 13, 14, 15, 17, 22,
27, 29, 29, 27, 26, 24, 21, 19, 18, 17, 16];

sum = 0;

for (let i = 0; i < temperatures.length; i++) {

sum += temperatures[i];

meanTemp = sum / temperatures.length;

console.log(`mean: ${meanTemp}`); // -> media: 18.083333333333332

Declaración de funciones
Al igual que con las variables, las funciones deben declararse antes de que podamos usarlas.
La sintaxis para la declaración de funciones se ve así:

function functionName() {
código
}
Este tipo de declaración de función en JavaScript se denomina declaración de función. Una
instrucción de función comienza con la palabra clave function seguida del nombre de la
función. Los nombres de las funciones deben seguir las mismas reglas que los nombres de las
variables y también deben ser significativos. Después del nombre de la función, hay
paréntesis que opcionalmente pueden tener parámetros de función, que discutiremos en un
momento. Después de los paréntesis viene el cuerpo de la función, que se compone de
cualquier cantidad de instrucciones (un bloque de código).

Intentemos declarar una función de acuerdo con estas reglas, que contendrá un fragmento de
nuestro código de programa que calcula la temperatura media diaria. Lo
llamaremos getMeanTemp. Por ahora, la función usará variables, declaradas fuera de ella (en
el contexto circundante). De hecho, prácticamente nunca se hace así, pero lo trataremos en
una de las etapas posteriores.

let temperatures;
let sum;
let meanTemp;

function getMeanTemp() {
sum = 0;
for (let i = 0; i < temperatures.length; i++) {
sum += temperatures[i];
}
meanTemp = sum / temperatures.length;
}

Hemos declarado nuestra función dándole un nombre y definiendo una secuencia de


instrucciones que debe realizar. Sin embargo, si intentaras ejecutar este código, no verías
ningún efecto. ¿Por qué? Porque declarar una función es solo una preparación. Para ejecutar
este código, tenemos que invocar o mandar llamar a la función.

Invocación de Funciones
Para invocar a una función, necesitamos escribir un nombre de función seguido de paréntesis. Por lo
tanto, nuestro ejemplo completo debería verse así:

let temperatures;
let sum;
let meanTemp;

function getMeanTemp() {
sum = 0;
for (let i = 0; i < temperatures.length; i++) {
sum += temperatures[i];
}
meanTemp = sum / temperatures.length;
}

temperatures = [12, 12, 11, 11, 10, 9, 9, 10, 12, 13, 15, 18, 21,
24, 24, 23, 25, 25, 23, 21, 20, 19, 17, 16];
getMeanTemp();
console.log(`mean: ${meanTemp}`); // -> mean: 16.666666666666668
temperatures = [17, 16, 14, 12, 10, 10, 10, 11, 13, 14, 15, 17,
22, 27, 29, 29, 27, 26, 24, 21, 19, 18, 17, 16];
getMeanTemp();
console.log(`mean: ${meanTemp}`); // -> mean: 18.083333333333332

Al principio del programa, después de la declaración de la variable, tenemos la declaración de la función


getMeanTemp. En la última parte del código, la invocamos dos veces escribiendo getMeanTemp(). Cada
llamada hace que el programa salte al código de la función, ejecute sus instrucciones y regrese a la
siguiente instrucción después de la llamada a la función. Sencillo, ¿no?

Por lo general, las funciones se declaran antes de llamarlas, principalmente al comienzo del código. Sin
embargo, esta es solo una buena práctica, que aumenta la legibilidad del código, no un requisito de
sintaxis de JavaScript. Las declaraciones de funciones se mueven automáticamente a la parte superior
del alcance, por lo que podemos usarlas antes de la declaración, siempre que estén en el alcance.

Entonces el código:

let name = Alice

function showName() {
console.log(name);
}

showName(); // -> Alice

Funcionará de la misma manera que:

let name = Alice

showName(); // -> Alice

function showName() {
console.log(name);
}

Ya sabemos qué es una declaración y una invocación de función. Es hora de echar un vistazo más de
cerca a su contenido. Comencemos con las variables que usamos.

Funciones - Variables Locales


Intentemos hacer un pequeño cambio en nuestro programa calculando la temperatura media.
¿Recuerdas qué son las variables locales? Así es como llamamos a las variables que se
declaran y usan en un ámbito o alcance limitado y no son visibles en todo el programa, lo que
significa que solo podemos usarlas dentro de ese ámbito en particular. Las variables
declaradas con la palabra clave let son locales dentro del bloque de código (es decir, dentro
del rango limitado por {}), mientras que las variables declaradas con la palabra clave var son
locales dentro del bloque de funciones. Entonces, si declaras una variable dentro de un bloque
de funciones, ya sea usando let o var, solo será visible (y utilizable) dentro del bloque de
funciones. Esto es muy útil porque, por lo general, las variables que se usan dentro de una
función no se necesitan fuera de ella.

En nuestro código, un ejemplo de tal variable es sum. Aunque la hemos declarado fuera de la
función getMeanTemp (es una variable global), solo la referimos dentro de la función. Por lo
tanto, una declaración global es innecesaria. Pongámoslo en orden, declarando sum
localmente.

El comportamiento del programa es el mismo, pero el código ha ganado algo de claridad. La


variable sum ahora es local, solo se puede usar dentro de la función getMeanTemp (que es
suficiente para nosotros, porque no se necesitaba para nada fuera de la función). En general,
debemos esforzarnos por mantener el código de la función lo más separado posible del
contexto que lo rodea, entre otras cosas, al no usar variables globales dentro de él. En nuestro
ejemplo, hay dos variables más: temperatures y meanTemp. Esta última, meanTemp, se usa
dentro de la función para almacenar y devolver el resultado calculado. Veámoslo.

let temperatures;

let meanTemp;

function getMeanTemp() {

let sum = 0;

for (let i = 0; i < temperatures.length; i++) {

sum += temperatures[i];

meanTemp = sum / temperatures.length;

temperatures = [12, 12, 11, 11, 10, 9, 9, 10, 12, 13, 15, 18, 21, 24,
24, 23, 25, 25, 23, 21, 20, 19, 17, 16];

getMeanTemp();

console.log(`mean: ${meanTemp}`); // -> mean: 16.666666666666668


temperatures = [17, 16, 14, 12, 10, 10, 10, 11, 13, 14, 15, 17, 22,
27, 29, 29, 27, 26, 24, 21, 19, 18, 17, 16];

getMeanTemp();

console.log(`mean: ${meanTemp}`); // -> mean: 18.083333333333332

La sentencia return
Las funciones que han sido invocadas ejecutan una secuencia de instrucciones contenidas en
su cuerpo. El resultado de esta ejecución puede ser un valor determinado que tal vez
queramos almacenar en alguna variable. La palabra clave return viene a ayudarnos en este
caso. ¿Para qué sirve exactamente return?

 Primero, hace que la función termine exactamente donde aparece esta palabra,
incluso si hay más instrucciones.
 Segundo, nos permite devolver un valor dado desde dentro de la función al lugar
donde fue invocada.

Alejémonos por un momento de nuestro ejemplo con el cálculo de la temperatura media y


juguemos con un código un poco más simple. La función showMsg contiene solo
dos console.logs separados por return.

function showMsg() {
console.log("mensaje 1");
return;
console.log("mensaje 2");
}

showMsg(); // -> mensaje 1

Como era de esperarse, la invocación termina mostrando solo el primer mensaje "mensaje 1",
luego el return interrumpe la función. En la práctica, usar return aquí no tendría mucho
sentido. Hace que nunca se ejecute console.log("mensaje 2"). Por lo tanto, sería más
fácil no escribir una segunda llamada console.log en lo absoluto.

Sin embargo, usando las instrucciones condicionales, podemos, por ejemplo, reaccionar a
errores dentro de la función y, en ciertas situaciones, interrumpir la función inmediatamente.

La sentencia return - continuación


Como dijimos, return nos permite no solo terminar una función. Si colocamos alguna expresión
(literal, variable, llamada de función) inmediatamente después de la palabra clave return, la función
completa devolverá el valor de esta expresión al lugar donde fue invocada o llamada. A continuación,
por ejemplo, se puede asignar el valor devuelto a una variable. Echemos un vistazo a un ejemplo de la
función getTrue.

En el ejemplo, declaramos una función simple getTrue, que siempre devuelve verdadero. Presta
atención a la invocación de la función: almacenamos el resultado en la variable test. Como puedes
adivinar, esta variable tendrá el valor booleano true o verdadero.
function getTrue() {
return true;
}

let test = getTrue();


console.log(test); // -> true

Volvamos al ejemplo con las temperaturas medias. Hasta ahora, los cálculos realizados dentro de la
función getMeanTemp se han realizado sobre la variable global meanTemp. Cambiaremos esto. Dentro
de la función, declararemos la variable local result, que contendrá el resultado calculado, y
usaremos return para devolverlo. La variable global meanTemp contendrá el resultado de la
invocación o llamada a la función, es decir, la primera vez, 16.666666666666668 y la segunda
vez, 18.0833333333333332.

let temperatures;
let meanTemp;

function getMeanTemp() {
let sum = 0;
let result;
for (let i = 0; i < temperatures.length; i++) {
sum += temperatures[i];
}
result = sum / temperatures.length;
return result;
}

temperatures = [12, 12, 11, 11, 10, 9, 9, 10, 12, 13, 15, 18, 21,
24, 24, 23, 25, 25, 23, 21, 20, 19, 17, 16];
meanTemp = getMeanTemp();
console.log(`mean: ${meanTemp}`);

temperatures = [17, 16, 14, 12, 10, 10, 10, 11, 13, 14, 15, 17,
22, 27, 29, 29, 27, 26, 24, 21, 19, 18, 17, 16];
meanTemp = getMeanTemp();
console.log(`mean: ${meanTemp}`);

La variable result en realidad solo se usa para almacenar temporalmente el valor que se devolverá.
Entonces podemos simplificar el código de la función aún más. Omitiremos la
variable result colocando la operación apropiada directamente después de return.

function getMeanTemp() {
let sum = 0;
for (let i = 0; i < temperatures.length; i++) {
sum += temperatures[i];
}
return sum / temperatures.length;
}
La variable meanTemp también se ha vuelto un poco redundante. Almacenamos el resultado de la
llamada de función en él, que luego se muestra en la consola. Esto también se puede simplificar
colocando la llamada de función getMeanTemp directamente en console.log (sin el uso de la
variable meanTemp).

let temperatures;

function getMeanTemp() {
let sum = 0;
for (let i = 0; i < temperatures.length; i++) {
sum += temperatures[i];
}
return sum / temperatures.length;
}

temperatures = [12, 12, 11, 11, 10, 9, 9, 10, 12, 13, 15, 18, 21,
24, 24, 23, 25, 25, 23, 21, 20, 19, 17, 16];
console.log(`mean: ${getMeanTemp()}`);

temperatures = [17, 16, 14, 12, 10, 10, 10, 11, 13, 14, 15, 17,
22, 27, 29, 29, 27, 26, 24, 21, 19, 18, 17, 16];
console.log(`mean: ${getMeanTemp()}`);

Nuestra getMeanTemp poco a poco empieza a parecerse a una función normal. Es una pieza de código
lógicamente independiente, devuelve un valor calculado y opera en variables locales. Queda un pequeño
problema por resolver. Contamos la media usando los datos contenidos en la variable global
temperatures. Y la función debe ser independiente de lo que sucede fuera de ella. Al mismo tiempo,
debería permitirnos calcular el valor medio para diferentes datos. ¿Cómo reconciliamos estas dos
demandas en conflicto? Los parámetros de la función se utilizan para esto.

Parámetros
En primer lugar, el uso de parámetros en funciones es opcional. Puede haber funciones que no tengan
parámetros, como hemos visto en nuestros ejemplos anteriores, al igual que puede haber funciones que
no devuelvan valores. Sin embargo, la mayoría de las veces creamos funciones que tienen parámetros
definidos y valores devueltos.

En JavaScript, los parámetros de una función aparecen en su declaración. Estos son nombres separados
por comas, colocados entre paréntesis después del nombre de la función. Cada parámetro dentro de una
función será tratado como una variable local. Una función cuya definición especifica los parámetros
debe invocarse de forma adecuada. Cuando se llama a una función de este tipo, los valores (literales,
variables, llamadas de función) deben colocarse entre paréntesis después de su nombre y se tratan como
parámetros dentro de la función. Los valores dados durante una llamada se llaman argumentos. Los
argumentos, si hay más de uno, se separan por comas y deben pasarse en el mismo orden que los
parámetros que definimos en la declaración de la función.

Veamos una función simple que suma dos valores. Lo llamaremos add.
function add(first, second) {
return first + second;
}

En la declaración de la función, entre paréntesis, ponemos dos parámetros: first y second. Los
nombres de los parámetros, al igual que las variables, deben estar relacionados con su propósito; en este
caso, lo hemos hecho de manera diferente para enfatizar que el orden de los parámetros es importante.
Dentro de la función de suma, estos parámetros se tratan como variables locales, cuyos valores se darán
cuando se invoque a la función.

let result = add(5, 7));


console.log(result); // -> 12

En el ejemplo, pasamos los argumentos 5 y 7 a la función. Así, durante la operación de la función, el


parámetro first tiene un valor de 5 y el parámetro second tiene un valor de 7. La función devuelve
el valor 12 a la variable result.

Puedes pasar cualquier tipo de datos como argumentos a la función, tanto simples como complejos.
Escribamos la función getElement, que devolverá el elemento seleccionado del arreglo, siendo el
arreglo y el índice del elemento los parámetros de la función.

function getElement(elements, index) {


return elements[index];
}

Llamémoslo con argumentos de muestra:

let names = ["Alice", "Bob", "Eve", "John"];


let name = getElement(names, 2);
console.log(name); // -> Eve

Volvamos al ejemplo con la temperatura media. La función getMeanTemp tomará un parámetro:


temperatures. Al mismo tiempo, eliminaremos del programa la variable global con este nombre y
crearemos otras dos, day1 y day2, que contendrán los datos de la medida. Estos datos se pasarán a la
función.

function getMeanTemp(temperatures) {
let sum = 0;
for (let i = 0; i < temperatures.length; i++) {
sum += temperatures[i];
}
return sum / temperatures.length;
}

let day1 = [12, 12, 11, 11, 10, 9, 9, 10, 12, 13, 15, 18, 21, 24,
24, 23, 25, 25, 23, 21, 20, 19, 17, 16];
console.log(`mean: ${getMeanTemp(day1)}`); // -> mean:
16.666666666666668

let day2 = [17, 16, 14, 12, 10, 10, 10, 11, 13, 14, 15, 17, 22,
27, 29, 29, 27, 26, 24, 21, 19, 18, 17, 16];
console.log(`mean: ${getMeanTemp(day2)}`); // -> mean:
18.083333333333332

La primera vez que se llama a la función getMeanTemp, la variable day1 se pasa a la


función getMeanTemp como argumento. Por lo tanto, los cálculos realizados dentro de la función
utilizando el parámetro de temperaturas se basarán en los valores de la variable day1. En la segunda
llamada, pasamos otro arreglo a la función, almacenado en la variable day2.

Probablemente ya puedas señalar una cosa más: al llamar al método console.log (una función
relacionada con el objeto de la consola) también le pasamos un argumento: una cadena que se mostrará
en la consola. Esto significa que hemos estado usando los parámetros de la función desde el comienzo
del curso.

Sombreado
Como mencionamos anteriormente, los parámetros se tratan dentro de la función como variables locales.
Y al igual que las variables locales explícitamente declaradas dentro de una función, sombrean las
variables globales del mismo nombre (o más generalmente, las variables del ámbito externo). Volvamos
por un momento al ejemplo de la suma de números. La función add tiene dos
parámetros: first y second. Al mismo tiempo, declararemos, fuera de la función, variables globales
denominadas first, second, third y fourth.

Si dentro de la función, nos referimos a la variable:

 first, será tratada como el parámetro con tal nombre que sombrea a la variable
global first (es decir, operaremos sobre el valor pasado como primer argumento)
 second, también será tratada como el parámetro de la función (el valor pasado como segundo
argumento)
 third, será tratada como una variable global, porque dentro de la función no hay una variable
local ni un parámetro con ese nombre.
 fourth, será tratada como global, al igual que third.

Por supuesto, fuera de la función, cada uno de estos nombres se referirá a variables globales.

function add(first, second) {


return first + second;
}

let first = 10, second = 20, third = 40, fourth = 80;


console.log(add(first, second)); // -> 30
console.log(add(second, third)); // -> 60
console.log(add(third, fourth)); // -> 120

Analiza el ejemplo cuidadosamente, anotando los valores específicos que se pasan a la función de suma
en cada una de las cuatro llamadas.

También intenta ejecutar y analizar un ejemplo más simple, en el que puede sombrear variables con
parámetros y variables locales.

let a = 100, b = 200, c = 300;

function test(a) {
let b = 10;
console.log(a); // parameter a
console.log(b); // local variable b
console.log(c); // global variable c
}

test(1); // -> 1
// -> 10
// -> 300

console.log(a); // -> 100


console.log(b); // -> 200
console.log(c); // -> 300

Validación de parámetros
¿Recuerdas que dijimos que a veces usamos la palabra clave return para interrumpir
funciones en caso de errores? Un buen ejemplo es la validación de parámetros de funciones.

Volvamos al ejemplo con la función getMeanTemp. La última versión que escribimos necesita
un arreglo de números como argumento. Antes de comenzar el cálculo, podemos verificar si el
valor que se le pasó es realmente un arreglo.

function getMeanTemp(temperatures) {
if (!(temperatures instanceof Array)) {
return NaN;
}
let sum = 0;
for (let i = 0; i < temperatures.length; i++) {
sum += temperatures[i];
}
return sum / temperatures.length;
}

console.log(getMeanTemp(10)); // -> NaN


console.log(getMeanTemp([10, 30])); // -> 20
Recursividad
Durante tus lecciones de matemáticas, probablemente te encontraste con el concepto de factoriales. Un
factorial es una función, indicada por un signo de exclamación en notación matemática. A esta función
le pasamos un número entero y su resultado se obtiene multiplicando todos los números enteros desde el
número 1 hasta el número dado como argumento. Formalmente, se define un factorial de la siguiente
manera:

n!=n x (n-1) x (n-2) x ... x 2 x 1

Entonces, por ejemplo, el factorial de 6 es:

6!=6 x 5 x 4 x 3 x 2 x 1 = 720

Intentemos escribir una función que calcule el factorial de un número dado. Tomará el parámetro n y
devolverá el valor calculado.

function factorial (n) {


let result = 1;
while (n > 1) {
result *= n;
n--;
}
return result;
}

console.log(factorial(6)); // -> 720

En este caso, usamos el método iterativo para calcular el factorial, es decir, usamos un bucle en el que,
durante cada iteración, multiplicamos el resultado anterior por otro elemento de la secuencia. Después de
la primera iteración, el resultado es 6, después de la segunda, 30, después de la tercera, 120, y así
sucesivamente. Las iteraciones se repiten hasta el último elemento significativo de la secuencia, es decir,
hasta el valor 2 (de ahí la condición de terminar el bucle n > 1).

Sin embargo, la definición de un factorial se puede escribir de una manera ligeramente diferente. Será el
factorial del elemento anterior n - 1 multiplicado por n.

Por ejemplo, 6! es 5! multiplicado por 6. Tal definición factorial usa la recursividad, es decir, se refiere a
una función a sí misma (pero con un argumento diferente). Una recursividad es un mecanismo que
permite simplificar la notación formal de muchas funciones matemáticas y presentarlas de forma
elegante. También podemos usar con éxito la recursividad en la programación.

Declaremos la función factorial nuevamente, esta vez usando la recursividad.

function factorial (n) {


return n > 1 ? n * factorial(n - 1) : 1;
}
console.log(factorial(6)); // -> 720

Para obtener un código más corto y compacto, en lugar de una instrucción condicional if, usamos el
operador condicional ternario. En él, verificamos si el argumento n es mayor que 1. Dependiendo de eso,
devolvemos el resultado de multiplicar el número n por el resultado de la llamada factorial (n - 1), o el
valor 1.

La recursividad se usa comúnmente en la programación. Sin embargo, como con cualquier solución, la
recursividad debe manejarse con cuidado. No deberíamos usarlo donde no podamos estimar la cantidad
de llamadas incrustadas. También debemos tener mucho cuidado al formular la condición que
interrumpirá las llamadas a la secuencia de funciones: los errores pueden hacer que el programa se
suspenda.

Funciones como miembro de primera clase


En JavaScript, las funciones son miembros de primera clase. Este término significa que las funciones
pueden tratarse como cualquier dato, que puede almacenarse en variables o pasarse como argumentos a
otras funciones. Por ejemplo, podemos declarar la función showMessage y luego almacenarla en la
variable sm.

function showMessage(message) {
console.log(`Mensaje: ${message}`);
}

let sm = showMessage;

Podemos almacenar cualquier función que sea accesible en este ámbito en una variable y usar un
operador de llamada de función () para ejecutarla. Podemos verificar que la variable sm ahora es una
función usando el operador typeof.

sm("¡Esto funciona!"); // -> Mensaje: ¡Esto funciona!


console.log(typeof sm); // -> function

Pero es importante recordar que cuando asignamos una función a una variable, no usamos un operador
de llamada de función, ya que ejecutaría la función y asignaría el resultado de la función a una variable,
y no a la función misma.

function doNothing() {
return undefined;
}

let a = doNothing(); // asignar el resultado de la llamada de función


let b = doNothing; // asignar una función
console.log(typeof a); // -> undefined
console.log(typeof b); // -> function

En el ejemplo, el resultado de la llamada a la función doNothing (es decir, el valor indefinido devuelto
por la función) se almacena en la variable a, mientras que la función doNothing en sí se almacena en la
variable b (o más precisamente, una referencia a la función se almacena en la variable b).

Esta propiedad es especialmente útil cuando se pasa la función como parámetro de llamada a otras
funciones, sobre las que pronto aprenderemos más. Por ahora, probemos que algo como esto es
realmente factible.

function add(a, b) {
return a + b;
}

function multiply(a, b) {
return a * b;
}

function operation(func, first, second) {


return func(first, second);
}

console.log(operation(add, 10, 20)); // -> 30


console.log(operation(multiply, 10, 20)); // -> 200

La función operation toma como primer argumento la función (parámetro func) y la llama con los otros
dos argumentos pasados (parámetros first y second).

Expresiones de funciones
Para almacenar una función en una variable o pasarla como argumento para llamar a una
función, no necesariamente tienes que declararla previamente y usar su nombre. Volvamos a
nuestro ejemplo con la función add:

function add(a, b) {
return a + b;
}

let myAdd = add;


console.log(myAdd(10, 20)); // -> 30
console.log(add(10, 20)); // -> 30
Primero declaramos la función add y luego la almacenamos en la variable myAdd. Podemos
llamar a la misma función usando tanto el nombre add como la variable myAdd. Podemos
acortar esta notación y declarar la función almacenándola en una variable.

let myAdd = function add(a, b) {


return a + b;
}

console.log(myAdd(10, 20)); // -> 30


console.log(add(10, 20)); // -> 30

En el ejemplo, declaramos nuevamente la función add y al mismo tiempo la almacenamos en


la variable myAdd. Esta forma de definir una función se llama expresión de función. En este
caso, es específicamente una expresión de función con nombre, porque la función tiene un
nombre (add). Si hay una expresión de función con nombre, probablemente también habrá
una expresión de función sin nombre, o precisamente, anónima. De hecho, simplemente
elimina el nombre que sigue a la palabra clave de función para cambiar la función de
nombrada a anónima.

let myAdd = function(a, b) {


return a + b;
}

console.log(myAdd(10, 20)); // -> 30

Expresiones de funciones - continuación


Volvamos al concepto de funciones anónimas. Puede parecer un poco incomprensible cuando
usas un nombre (aunque es un nombre de variable) para referirse a una función. En este
caso, se trata de anonimato, es decir, la falta de un nombre, en la definición misma de una
función. Esto será mucho más evidente al pasar una función como parámetro de llamada a
otra función. Veamos el ejemplo:

function operation(func, first, second) {


return func(first, second);
}

let myAdd = function(a, b) {


return a + b;
}

console.log(operation(myAdd, 10, 20)); // -> 30

console.log(operation(function(a, b) {
return a * b;
}, 10, 20)); // -> 200

En el primer paso, declaramos la operación de la función (es una función con nombre, y aquí
usamos la declaración de función, así que nos referiremos a la función por su nombre). En el
siguiente paso, definimos una función anónima, que almacenamos en la
variable myAdd (usamos una expresión de función). Llamamos a la función de operación,
pasando la función myAdd y los valores 10 y 20 como argumentos.

El resultado es fácil de predecir. Solo se vuelve interesante cuando llamamos a la


función operation nuevamente. Esta vez, el primer argumento es la función anónima
(nuevamente la expresión de la función), que se define directamente en una llamada de
operación. El resultado es una multiplicación, aunque el nombre de la nueva función (o la
variable en la que se podría colocar) no aparecerá por ningún lado. La función se ha definido
solo para pasarla una vez a la función operation. A primera vista, puede parecer un
mecanismo completamente inútil, pero en el mundo real se usa con mucha frecuencia.

Expresiones de funciones - continuación


Volvamos al concepto de funciones anónimas. Puede parecer un poco incomprensible cuando
usas un nombre (aunque es un nombre de variable) para referirse a una función. En este
caso, se trata de anonimato, es decir, la falta de un nombre, en la definición misma de una
función. Esto será mucho más evidente al pasar una función como parámetro de llamada a
otra función. Veamos el ejemplo:

function operation(func, first, second) {


return func(first, second);
}

let myAdd = function(a, b) {


return a + b;
}

console.log(operation(myAdd, 10, 20)); // -> 30

console.log(operation(function(a, b) {
return a * b;
}, 10, 20)); // -> 200

En el primer paso, declaramos la operación de la función (es una función con nombre, y aquí
usamos la declaración de función, así que nos referiremos a la función por su nombre). En el
siguiente paso, definimos una función anónima, que almacenamos en la
variable myAdd (usamos una expresión de función). Llamamos a la función de operación,
pasando la función myAdd y los valores 10 y 20 como argumentos.

El resultado es fácil de predecir. Solo se vuelve interesante cuando llamamos a la


función operation nuevamente. Esta vez, el primer argumento es la función anónima
(nuevamente la expresión de la función), que se define directamente en una llamada de
operación. El resultado es una multiplicación, aunque el nombre de la nueva función (o la
variable en la que se podría colocar) no aparecerá por ningún lado. La función se ha definido
solo para pasarla una vez a la función operation. A primera vista, puede parecer un
mecanismo completamente inútil, pero en el mundo real se usa con mucha frecuencia.

Callbacks
Las funciones que se pasan como argumentos a otras funciones pueden parecer bastante
exóticas y no muy útiles, pero de hecho, son una parte muy importante de la programación.
Tan importante que incluso tienen su propio nombre. Son funciones callback. Como hemos
visto en ejemplos anteriores, una función que recibe una callback como argumento puede
llamarla en cualquier momento. Es importante destacar que, en nuestros ejemplos, la callback
se ejecuta de forma síncrona, es decir, se ejecuta en un orden estrictamente definido que
resulta de dónde se coloca entre las otras instrucciones.

Callbacks síncronas

La ejecución síncrona es la forma más natural de ver cómo funciona el programa. Las
instrucciones posteriores se ejecutan en el orden en que se colocan en el código. Si llama a
una función, las instrucciones que contiene se ejecutarán en el momento de la llamada. Si le
pasamos otra función a esta función como argumento, y también la llamamos dentro de una
función externa, entonces todas las instrucciones mantendrán su orden natural.

let inner = function() {

console.log('inner 1');

let outer = function(callback) {

console.log('outer 1');

callback();

console.log('outer 2');

console.log('test 1');

outer(inner);

console.log('test 2');

La ejecución del código anterior hará que la consola imprima el siguiente texto en este orden
exacto:

test 1

outer 1
inner 1

outer 2

test 2

salida

Por lo tanto, se mantiene el orden de acciones resultante del orden de llamada de los
comandos y funciones. Sin embargo, este orden puede verse alterado en determinadas
circunstancias especiales.

Callbacks asíncronas
El funcionamiento asíncrono de programas es un tema bastante complejo, que depende en
gran medida de un lenguaje de programación en particular y, a menudo, también del entorno.

En el caso de que JavaScript del lado del cliente se ejecute en un navegador, se limita a la
programación basada en eventos, es decir, la respuesta asincrónica a ciertos eventos. Un
evento puede ser una señal enviada por un temporizador, una acción del usuario (por ejemplo,
presionar una tecla o hacer clic en un elemento de interfaz seleccionado) o información sobre
la recepción de datos del servidor.

Usando las funciones apropiadas, combinamos un tipo específico de evento con una función
callback seleccionada, que se llamará cuando ocurra el evento.

Uno de los casos más simples cuando existe una ejecución asíncrona de instrucciones es el
uso de la función setTimeout. Esta función toma otra función (una callback) y el tiempo
expresado en milisegundos como argumentos. La función callback se ejecuta después del
tiempo especificado y, mientras tanto, se ejecutará la siguiente instrucción del programa
(ubicada en el código después del setTimeout).

Por lo tanto, el momento en que se llama a la función callback no está determinado por su
orden, sino por un retraso impuesto arbitrariamente. El retraso solo se aplica a la función
callback dado a setTimeout, mientras que el resto del código aún se ejecuta de forma
síncrona.

Vamos a modificar un poco el ejemplo anterior. En la función outer, no llamamos


a callback() inmediatamente, sino que lo pasamos a setTimeout, que lo ejecuta con un
retraso de 1000 milisegundos (un segundo).

El resultado es en realidad un poco diferente al que observamos en el ejemplo anterior, ya que


esta vez aparecerá la siguiente secuencia de mensajes en la consola (el último con un retraso
de un segundo):

test 1
outer 1
outer 2
test 2
...
inner 1
let inner = function() {

console.log('inner 1');

let outer = function(callback) {

console.log('outer 1');

setTimeout(callback, 1000) /*ms*/;

console.log('outer 2');

console.log('test 1');

outer(inner);

console.log('test 2');

Las funciones setTimeout y setInterval


La función setTimeout se utiliza cuando deseas provocar una acción retrasada. Una función
similar es setInterval. Esta vez, la acción también se realiza con retraso, pero
periódicamente, por lo que se repite a intervalos fijos. Mientras tanto, se ejecuta el programa
principal y, en el momento especificado, se llama a la función callback proporcionada como
argumento para una llamada a setInterval.

Curiosamente, la función setInterval devuelve un identificador durante la llamada, que se


puede usar para eliminar el temporizador utilizado en ella (y, en consecuencia, para detener la
llamada cíclica de la función callback). Haremos esto en el siguiente ejemplo. Primero,
ejecutamos setInterval, que llamará a la función callback (es decir, la función inner) en
intervalos de un segundo. Luego llamamos a setTimeout, que apagará el temporizador
asociado con el llamado previamente a setInterval después de 5.5 segundos. Como
resultado, la función inner debe llamarse cinco veces. Mientras tanto, se ejecutará el resto
del programa...

El resultado de la ejecución del programa debe ser los siguientes mensajes, que aparecerán
en la consola:

outer 1
outer 2
test 2
...
inner 1
inner 1
inner 1
inner 1
inner 1

let inner = function() {

console.log('inner 1');

let outer = function(callback) {

console.log('outer 1');

let timerId = setInterval(callback, 1000) /*ms*/;

console.log('outer 2');

setTimeout(function(){

clearInterval(timerId);

}, 5500);

console.log('test 1');

outer(inner);

Callbacks asíncronas - continuación


Si ejecutamos el código JavaScript en el lado del cliente, en el navegador, siempre está
asociado con el sitio web. La ventana en la que se encuentra esta página se representa en el
JavaScript del lado del cliente mediante una variable de ventana global. El objeto ventana
tiene un método (o su propia función) llamado addEventListener. Esta función te permite
registrar una determinada acción para que se realice en respuesta a un evento relacionado
con la ventana. Dicho evento puede ser un "clic", que es un solo clic del mouse en cualquier
lugar de la página (hay un conjunto limitado de eventos con nombre asociados con un objeto
en particular, al que puede reaccionar). La acción a realizar se pasa al método
addEventListener como una función callback.

window.addEventListener("click", function() {
console.log("¡hizo clic!");
});
Intenta ejecutar el código de muestra. Nada especial debe suceder inmediatamente después
de que se inicie. Solo cuando haga clic en cualquier parte de la página, aparecerá un mensaje
en la consola: "¡hizo clic!". No se llama a nuestra función hasta que se produce el evento
"click", que es absolutamente asíncrono. Mientras tanto, entre clics posteriores, se podría
ejecutar el resto del programa, si tuviéramos el antojo de escribirlo.

De hecho, no es muy buena idea conectar una respuesta de clic a un objeto de ventana. La
mayoría de las veces, tales acciones están asociadas a elementos específicos de la interfaz
(botones, casillas de verificación, etc.) que permiten su diferenciación. Sin embargo, como ya
dijimos, volveremos a este tema en la siguiente parte del curso; esto es solo para demostrar
una llamada de función con un evento generado por el usuario.

Funciones arrow o flecha


Una función flecha es una forma más corta de una expresión de función. Una expresión de función
flecha se compone de: paréntesis que contienen de cero a varios parámetros (si está presente
exactamente un parámetro, se pueden omitir los paréntesis); una flecha que se ve así "=>"; y el cuerpo
de la función, que puede estar entre llaves {} si el cuerpo es más largo. Si una función flecha tiene solo
una sentencia y devuelve su valor, podemos omitir la palabra clave return, ya que se agregará
implícitamente. Por ejemplo, la función add, que ya conocemos:

let add = function(a, b) {


return a + b;
}
console.log(add(10, 20)); // -> 30

se puede escribir de la siguiente manera:

let add = (a, b) => {


return a + b;
}
console.log(add(10, 20)); // -> 30

o simplificado aún más (la función tiene solo una sentencia, cuyo valor regresa):

let add = (a, b) => a + b;


console.log(add(10, 20)); // -> 30

Si la función flecha toma exactamente un parámetro, se pueden omitir los paréntesis. Volvamos a los
ejemplos con la función recursiva factorial, que toma exactamente un parámetro, n. En el ejemplo
anterior, lo declaramos usando la sentencia de función que ya hemos empleado:

function factorial(n) {
return n > 1 ? n * factorial(n - 1) : 1;
}
console.log(factorial(5)); // -> 120

Usando la expresión de la función flecha, podemos escribirla en una forma aún más compacta. Toma en
cuenta que esta vez, el parámetro no se proporciona entre paréntesis (nuevamente, si la función flecha
toma exactamente un parámetro, se pueden omitir los paréntesis). Dado que la función devuelve el
resultado de exactamente una instrucción, también se puede omitir la palabra clave return.

La expresión flecha se usa principalmente para funciones cortas, a menudo anónimas, que pueden
presentarse como aún más compactas en esta forma. Se diferencian de las funciones ordinarias por una
cosa más aparte de la forma de notación, en otras palabras, cómo se interpreta la palabra
clave this dentro de ellas. Sin embargo, este es un tema relacionado con la programación orientada a
objetos más avanzada, que está mucho más allá del alcance de este curso. Por lo tanto, podemos suponer
que ambas formas de definir funciones, es decir, expresión de función y expresión de función flecha, te
permiten definir funciones que funcionan de manera idéntica.

Un ejemplo típico del uso de funciones flecha es el método forEach, disponible en datos de
tipo Array. Hemos aprendido varias formas de pasar a través de los elementos del arreglo, utilizando
diferentes tipos de bucles. El método forEach es otro, y francamente hablando, actualmente el más
utilizado. Este método toma como argumento... una función.

Esta función se llamará cada vez para cada elemento del arreglo. Podemos crear cualquier función para
este propósito. Hay una condición, que debe tener al menos un parámetro, que será tratado como un
elemento visitado del arreglo (la sintaxis de esta función puede ser un poco más compleja, pero la
explicaremos en la siguiente parte del curso). Veamos un ejemplo sencillo:

let names = ['Alice', 'Eve', 'John'];


function showName(element) {
console.log(element);
}
names.forEach(showName); // -> Alice, Eve, John

La función showName se ha pasado como argumento de llamada al método forEach del arreglo
names. Por lo tanto, showName será llamado tres veces, por cada elemento del arreglo de names, y en
cada llamada su parámetro será igual al nombre sucesivo, es decir a su vez: Alice , Eva y Juan. Lo
único que tiene que hacer showName es mostrar el element (name) recibido.

Se puede lograr el mismo efecto pasando una función flecha anónima al método forEach. Ni siquiera lo
almacenamos en una variable, porque asumimos que la usaremos solo aquí y no la volveremos a
consultar.

Resumen
Las funciones son uno de los elementos más fundamentales de la programación en la mayoría
de los lenguajes y es difícil imaginar escribir un programa sin ellas. En nuestro curso, los
hemos estado usando desde el principio; después de todo, el método log del objeto de la
consola (que es simplemente console.log) también es una función. Entonces usamos
funciones creadas y proporcionadas por otros programadores. En el caso de console.log, en
realidad es una parte integral del lenguaje provisto como una función, pero hay muchas
funciones que brindan alguna funcionalidad nueva y están escritas por terceros (empresas,
organizaciones o desarrolladores privados). Estas funciones suelen estar disponibles en forma
de bibliotecas (es decir, conjuntos de funciones), que se dedican a resolver una clase
específica de problemas, por ejemplo, Leaflet (mapas), D3.js (visualización de datos) o jQuery
(manipulación DOM).

Una cosa es usar las funciones listas para usar, pero otra muy distinta es escribirlas para tus
propias necesidades. Las funciones permiten, entre otras cosas, una división lógica más
sencilla del programa, reutilizando el mismo código y probando partes seleccionadas del
mismo. En este capítulo, se te ha familiarizado con diferentes métodos para definir funciones
(declaración clásica usando la declaración de función, expresiones de función o expresiones
de función flecha), pasándoles parámetros y devolviendo valores de ellos. Has visto la
diferencia entre funciones nombradas y anónimas, se te ha familiarizado con el concepto de
funciones callback y has visto cómo entendemos la recursividad en una función. Entonces, si
es necesario, podrás no solo escribir tu propia función, sino también personalizar la forma en
que la defines y la usas. Por supuesto, este capítulo no agota el tema de las funciones en
JavaScript, pero es una buena base para ejercicios posteriores que te permitirán comenzar a
usar funciones mientras programas.

Errores y excepciones
Es muy importante que te prepares para esta simple verdad:

Los errores ocurrirán

Ciertamente has sido testigo, en múltiples ocasiones, de diferentes aplicaciones que funcionan mal, se
vuelven inestables, arrojan resultados inesperados o incluso se apagan sin control. Desafortunadamente,
nosotros, los programadores, somos responsables de la mayoría de estos problemas. Incluso si no
causamos estos problemas directamente, probablemente no anticipamos ciertas situaciones que podrían
conducir a un mal funcionamiento del programa (por ejemplo, falta de conexión a la red).

Ocurrirán errores en el funcionamiento de los programas. Tenemos que aceptar eso, mientras tratamos
de minimizar su número y mitigar el daño que potencialmente pueden causar. Podemos cometer errores
en cada etapa del desarrollo de software, desde un diseño incorrecto hasta errores tipográficos comunes
en el código que escribimos. Los errores serán el resultado de una concepción errónea al tratar de
resolver un determinado problema, el mal uso del lenguaje de programación, o la incapacidad de
predecir comportamientos extraños del usuario. Desafortunadamente, los errores en el código que causan
errores son una parte integral de la programación.

Este hecho se expresa mejor con las palabras de uno de los fundadores de la informática moderna,
Edsger W. Dijkstra: "Si la depuración es el proceso de eliminar errores de software, entonces la
programación debe ser el proceso de colocarlos".
Lenguajes naturales y errores de comunicación

Los lenguajes de programación no se llaman lenguajes por casualidad. Al igual que los lenguajes
naturales, los lenguajes que usamos para comunicarnos con otras personas, los lenguajes de
programación se usan para formular con precisión sentencias (instrucciones) interpretables sin
ambigüedades. Y al igual que los lenguajes naturales, tienen su gramática y vocabulario.

La gramática, o formalmente, la sintaxis de un lenguaje de programación, es un conjunto de reglas que


definen la estructura de las instrucciones (es decir, las oraciones del lenguaje natural). Estas reglas
suelen ser muy precisas y especifican, por ejemplo, el orden en el que escribimos determinadas palabras
clave u operadores.

Cada lenguaje también tiene su propio vocabulario limitado, que es una lista de palabras que se pueden
usar para construir instrucciones (es decir, nuevamente, las oraciones de un lenguaje natural). Esta
imagen está bastante simplificada, pero debería permitirnos comprender qué errores pueden ocurrir al
usar el lenguaje. Para comenzar, intentaremos presentar la diferencia entre varias categorías de errores
usando un lenguaje natural.

Lenguajes naturales y errores de comunicación


Imagina que estás organizando una fiesta junto al lago para tus amigos. Como organizador, les explicas a
todos cómo llegar allí, pero como siempre en tales situaciones, alguien se pierde. Casi llegan, pero luego
envían un mensaje de texto pidiendo instrucciones adicionales. Envías una respuesta, en la que les dices
que giren a la derecha en el primer camino después de salir del bosque, y luego conduzcan otros 500
metros. Como todavía estás en camino, les pides que esperen en el lugar. El mensaje correcto podría
verse así:

Después de dejar el bosque, gira a la derecha hacia el primer camino y


conduce 500 m. Esperame en el lugar.

Esta es una oración que podemos tratar como una instrucción, que describe sin ambigüedades un
determinado procedimiento. ¿Qué pasaría si nos apresuráramos al escribir esta información?
Comencemos con los signos de puntuación que faltan:

después de salir del bosque gira a la derecha hacia el primer camino y


conduce 500 m esperame en el lugar

Éste es un ejemplo de un error de sintaxis (o más precisamente, errores). En español, una oración
declarativa debe terminar con un punto. Probablemente la persona que reciba este mensaje adivine
fácilmente de qué se trata, pero formalmente será incorrecto y ambiguo. El intérprete (p.ej. el motor de
JavaScript) o el compilador no pueden adivinar el significado de lo que hemos escrito. Si se produce un
error de este tipo, será necesario que lo corrijamos. Dichos errores suelen ser muy fáciles de detectar
automáticamente y siempre deben corregirse. Violan las reglas de la sintaxis del lenguaje. El programa
no se ejecutará si contiene un error de sintaxis.

Recuperemos los signos de puntuación, pero cambiemos una de las palabras, reemplazando "camino"
con "cmno".

Después de dejar el bosque, gira a la derecha hacia el primer cmno, y


conduce 500 m. Esperame en el lugar.

Nuevamente, el destinatario probablemente adivinará de qué se trata, pero el intérprete no puede darse el
lujo de adivinar qué significa la palabra "cmno", porque no conoce el significado de esa palabra. Tal
error también es fácil de detectar, porque la palabra "cmno" no está en el vocabulario de nuestro
lenguaje. Se trata de un error semántico. En lenguajes de programación compilados, este tipo de error
no permitirá la compilación y ejecución del programa. En JavaScript, el intérprete iniciará el programa y
detendrá su ejecución después de llegar a dicha instrucción. Este tipo específico de error semántico en
JavaScript se denomina error de referencia.

¿Qué sucede si reemplazamos una palabra por un error tipográfico con una que existe en nuestro
diccionario?

Después de dejar el bosque, gira a la derecha hacia


el primitivo camino, y conduce 500 m. Esperame en el lugar.

Esta vez hemos cambiado la palabra "primero" por la palabra "primitivo". Si una persona analiza la
oración, sentirá que algo está mal y comenzará a buscar un error: la palabra "primitivo" no coincide en
absoluto con la oración, y probablemente adivinará con qué reemplazarla. Este tipo de error ya no será
tan fácil de detectar para un intérprete. La palabra está en el vocabulario, y el análisis tiene que hacerse
en un contexto más amplio. También es un error semántico.

La última categoría son los errores lógicos. Son, por mucho, los más difíciles de encontrar, porque desde
un punto de vista formal, todo parecerá correcto. Deberíamos decirle a nuestro amigo que gire a la
derecha, pero ocupado con otra cosa nos apresuramos a escribir... a la izquierda.

Después de dejar el bosque, gira a la izquierda hacia el primer


camino, y conduce 500 m. Esperame en el lugar.

Formalmente, todo parece correcto: sintaxis, vocabulario, contexto. La información es consistente y sin
ambigüedades, pero obviamente incorrecto. El error no se detectará hasta que alguien intente seguir esta
instrucción y desaparezca en algún lugar del desierto.

Los dos últimos errores pueden parecer bastante similares a primera vista, pero describen dos situaciones
completamente diferentes. Un error lógico hace posible ejecutar la instrucción, pero dará un resultado
erróneo. Una instrucción con un error semántico no tendrá sentido, por lo que lo más probable es que no
sea posible ejecutarla de esta forma.

Errores y excepciones en JavaScript


Intentemos generar errores sintácticos, semánticos y lógicos en JavaScript, para realizar pruebas de
forma controlada. Digamos que queremos escribir una función arrow simple llamada multiply, que
multiplicará los dos argumentos proporcionados:

let multiply = (a b) => a + b; // -> Uncaught SyntaxError:


Unexpected identifier
let result = multiply(10, 20);
console.log(result);
En el ejemplo, hay un error de sintaxis típico: nos hemos olvidado de la coma entre los parámetros de la
función. El error lo detecta el motor JavaScript, que no nos permite ejecutar el programa. Lo corregimos,
pero también cometemos otro error:

let multipl = (a, b) => a + b;


let result = multiply(10, 20); // -> Uncaught ReferenceError:
multiply is not defined
console.log(result);

Esta vez, tenemos un error tipográfico en el nombre de la función declarada: en vez de multiply,
hemos escrito multipl. En la llamada de función, usamos el nombre multiply, que no existe. Esto es
un error semántico, en este caso fácil de detectar, porque no existe ninguna función con este nombre. La
ejecución del programa se interrumpe en la línea del error. Presta atención a dos cosas. En primer lugar,
los mensajes de error que se muestran en la consola determinan con bastante precisión qué y dónde algo
falla; lee esta información detenidamente, ya que ayudará a eliminar el error. La segunda cosa es el
comienzo del mensaje: Uncaught .... Si un error puede no contenerse, probablemente se pueda
contenter. Y efectivamente se puede, como veremos en un momento.

Sin embargo, corrijamos el error y ejecutemos el programa nuevamente:

let multiply = (a, b) => a + b;


let result = multiply(10, 20);
console.log(result); // -> 30 ?

¡Éxito, no se han cometido errores! Pero... el resultado es un poco sospechoso: 30 ciertamente no es el


resultado de multiplicar 10 por 20. Por supuesto, el código todavía no es correcto, ya que se supone que
la función se usa para la multiplicación, pero por error hemos insertado un signo de suma en lugar de
multiplicación. Este es un error lógico típico, imposible de detectar automáticamente. Desde un punto de
vista formal, todo está construido correctamente, pero la lógica de nuestra función es incorrecta (estamos
haciendo que haga algo diferente a lo que pretendíamos). El intérprete de JavaScript no puede detectar
tales errores, porque no puede saber lo que planeamos lograr al escribir dicha función.

Vamos a corregirlo una vez más:

let multiply = (a, b) => a * b;


let result = multiply(10, 20);
console.log(result); // -> 200

Cuando JavaScript detecta errores sintácticos o semánticos, genera y arroja objetos específicos que
contienen información sobre el error encontrado. Por lo general, en tal situación, decimos que se ha
producido un error. En el caso de errores de sintaxis, el motor de JavaScript no nos permite ejecutar el
programa, y en la consola recibimos información sobre lo que es incorrecto. Los errores que no sean de
sintaxis (por ejemplo, errores semánticos) generalmente se denominan errores de tiempo de
ejecución en JavaScript. Aparecen mientras se ejecuta el programa. También podemos
llamarlas excepciones. De forma predeterminada, las excepciones lanzadas interrumpen la ejecución del
programa y hacen que aparezca la información adecuada en la consola (observamos esto en nuestro
ejemplo con la función multiply). Generemos de nuevo la situación errónea:

console.log('abc'); // -> abc


conole.log('def'); // -> Uncaught ReferenceError: conole is not
defined
console.log('ghi');

El error tipográfico en la palabra console es un error semántico, en JavaScript


llamado ReferenceError. JavaScript no conoce la palabra conole. Como puedes ver, el programa
dejará de funcionar solo en la segunda línea y aún podrá ejecutar la primera línea correctamente. Es
posible evitar que el programa se detenga en tal situación. Esto se llama manejo de excepciones (o más
generalmente, manejo de errores). Para manejar excepciones generadas en JavaScript (como en muchos
otros lenguajes) usamos la instrucción try ... catch.

try {
console.log('abc'); // -> abc
conole.log('abc');
} catch (error) {
console.log(error.message); // -> conole is not defined
}

Si se lanza una excepción en el bloque de código después de la palabra clave try, el programa no se
interrumpe por completo, sino que salta a la parte del código después de la palabra clave catch y
continúa desde allí. Echaremos un vistazo más de cerca a esta construcción en breve.

¿Errores sin excepciones?


En JavaScript, no todas las situaciones erróneas arrojan excepciones. Muchos de ellos se
manejan de una manera ligeramente diferente. El mejor ejemplo son los errores aritméticos.

console.log(100 / 0); // -> Infinity


console.log(100 * "2"); // -> 200
console.log(100 * "abc"); // -> NaN

Ninguno de los comandos anteriores generará una excepción, aunque no parecen la


aritmética más correcta. Dividir entre cero dará como resultado un valor Infinity. La
multiplicación de un número por una cadena, que representará un número, convertirá
automáticamente esta cadena en un número (y luego realizará la multiplicación). Un intento de
realizar una operación aritmética en una cadena que no representa un número (es decir, que
no se puede convertir) dará como resultado NaN (no un número). Al menos dos de estos casos
son claramente incorrectos (el primero y el tercero), pero en lugar de excepciones, la
información sobre el error es el valor específico que se devuelve. Veamos un ejemplo más:

console.log(Math.pow("abc", "def")); // -> NaN


Esta vez, usamos el método pow de Math, que se usa para elevar un número dado a la
potencia dada. El objeto Math se discutirá en las siguientes partes del curso, pero en este
punto es suficiente para nosotros decir que Math.pow es simplemente una función que toma
dos números como argumentos y devuelve el resultado de su potencia. Sin embargo, las dos
cadenas de caracteres que hemos proporcionado a esta función son números difíciles de
llamar. Sin embargo, la función no genera una excepción, sino que devuelve el valor NaN.

La conclusión es bastante simple: si estás aprendiendo sobre una nueva función u operador,
debes verificar en la documentación (por ejemplo, en la página de MDN) cómo se comportan
en caso de errores. Algunos de ellos generarán excepciones, mientras que otros devolverán
algunos valores específicos. Dependiendo de eso, podrás prepararte adecuadamente para
manejar errores utilizando el método try o instrucciones condicionales simples. Por cierto, para
los ejemplos que se acaban de mostrar, la solución más sensata sería comprobar si los
valores proporcionados realmente son números (¿recuerdas el operador typeof?).

Confianza Limitada
Los programas no se ejecutan en el vacío. Por lo general, durante su ejecución, hay
interacciones con los usuarios (p. ej., ingresar datos necesarios para calcular ciertos valores)
u otros programas o sistemas (p. ej., descargar datos del servidor). El comportamiento tanto
de los usuarios como de otros sistemas debe tratarse con precaución, y no podemos asumir
que el usuario proporcionará datos en el formato que requerimos, o que el servidor de datos
siempre funcionará. Tales situaciones inesperadas también serán fuentes de errores en
nuestro programa. Y aunque no dependen directamente de nosotros, es nuestra
responsabilidad anticiparnos a situaciones potencialmente peligrosas. Si, por ejemplo,
escribimos una calculadora en la que el usuario ingresa sus valores, entonces probablemente
deberíamos verificar si el divisor no es un cero antes de hacer la división. En teoría, el usuario
debe saber que no dividimos entre cero, pero somos responsables de la estabilidad del
programa. No le creas al usuario ni a otros sistemas. Debes suponer qué puede salir mal y
verifica los datos recibidos antes de usarlos en tu programa.

Vamos a escribir un fragmento de código que te pedirá que introduzcas dos números. Luego
queremos mostrar el resultado de dividir el primer número entre el segundo:

let sX = prompt("Ingrese el primer número");


let sY = prompt("Ingrese el segundo número");
let x = Number(sX);
let y = Number(sY);
if (Number.isFinite(x) && Number.isFinite(y) && y !== 0) {
console.log(x / y);
} else {
console.log("Argumentos incorrectos");
}

Probablemente recuerdes la función prompt, que muestra un cuadro de diálogo en el que


podemos ingresar un valor. Prompt devolverá el valor ingresado, siempre como una cadena
(incluso si el usuario ingresa un número, por ejemplo, ingresa 1024, pero obtenemos la
cadena "1024"). Estamos convirtiendo explícitamente una cadena de este tipo en un número
usando el constructor Number (esto se discutirá en detalle en el próximo curso). Como no le
creemos al usuario, predecimos que en lugar de un número, podría haber dado una cadena
como "abcd", o un segundo valor igual a "0". Por lo tanto, antes de realizar la división,
verificamos si podemos aceptar los valores convertidos. Usamos el
método Number.isFinite para este propósito. Devuelve true si el argumento es un número
correcto, y false si no lo es, por ejemplo Infinity o NaN . Además, comprobamos si el
divisor no es cero.

Algunos detalles más sobre errores y excepciones de


JavaScript
Tratemos de organizar la información sobre errores y excepciones, y sobre todo, su manejo.
Esta vez, veamos el problema desde un punto de vista estrictamente funcional.
Comenzaremos con una descripción general de los tipos de errores más importantes
detectados por JavaScript, analizaremos con más detalle la sentencia try... catch y
mostraremos que también podemos lanzar excepciones directamente.

Tipos básicos de errores.

Existen algunos tipos de errores subyacentes que produce JavaScript. La mayoría de las
veces, especialmente al principio, encontrarás errores de sintaxis y de referencia. También
discutiremos los errores de tipo y rango.

1. SyntaxError

Como dijimos anteriormente, un SyntaxError aparece cuando un código está mal formado,
cuando hay errores tipográficos en las palabras clave, paréntesis o corchetes que no
coinciden, etc. El código ni siquiera se puede ejecutar, ya que JavaScript no es capaz de
entenderlo. Por lo tanto, se lanza el error correspondiente antes de que se inicie el programa.

"use strict";

iff (true) { //-> Uncaught SyntaxError: Unexpected token '{'

console.log("true");

En el ejemplo anterior, cometimos un error tipográfico en la palabra clave if y agregamos una


letra f adicional. El motor de JavaScript trata el nombre desconocido como una llamada de
función (por los paréntesis después del iff) y se sorprende por la presencia de una llave.

2. ReferenceError
Ya hemos visto este error. Ocurre cuando intentamos acceder a una función o variable que no
existe. El motor de JavaScript no conoce el significado del nombre dado, por lo que es un
error que calificaremos como un error semántico. La excepción correspondiente se lanza en el
momento de la ejecución del programa, cuando se alcanza la instrucción incorrecta (es decir,
en JavaScript, los errores semánticos son errores de tiempo de ejecución).

let a = b; // -> Uncaught ReferenceError: b is not defined

El intento de declarar la variable a no tiene éxito porque, al mismo tiempo, queremos


inicializarla con el valor de la variable b. La variable b no se ha declarado en ninguna parte
antes, por lo que el motor de JavaScript no conoce este nombre.

fun(); // -> Uncaught ReferenceError: fun is not defined

Esta vez, hemos intentado llamar la función fun. Si no la hemos declarado antes, y no hay
ninguna función con este nombre entre las funciones estándar de JavaScript, la llamada
finaliza con un error.

3. TypeError
Este tipo de error se produce cuando un determinado valor no es del tipo esperado (es decir,
intenta realizar una operación que no es aceptable). Los ejemplos típicos son cambiar el valor
constante o verificar la longitud de una variable que no es una cadena. Este error es
particularmente importante cuando se trabaja con objetos, lo cual está fuera del alcance de
este curso (hablaremos de ellos en la siguiente parte del curso). Este es un run-time
error típico, por lo que se lanzará la excepción apropiada mientras se ejecuta el programa,
después de llegar a la instrucción problemática.

const someConstValue = 5;
someConstValue = 7; // -> Uncaught TypeError: Assignment to constant
variable.

Intentar almacenar el nuevo valor en la constante someConstValue falló por razones obvias, lo
que resultó en un TypeError.

let someNumber = 10;


someNumber.length(); // -> Uncaught TypeError: someNumber.length is
not a function

Esta vez, hemos intentado tratar el contenido de la variable someNumber como una cadena y
verificar su longitud. El motor de JavaScript nota que la variable almacena un número, y tal
operación no está permitida.

4. RangeError
Este tipo de error se genera cuando pasas un valor a una función que está fuera de su rango
aceptable.
Nuevamente, es un run-time error, y la excepción se lanza mientras el programa se está
ejecutando, después de llegar a la instrucción incorrecta. De hecho, esta excepción es más
útil al escribir tus propias funciones y manejar errores. Luego puedes lanzar una excepción en
ciertas situaciones.

let testArray1 = Array(10);

console.log(testArray1.length); // -> 10

let testArray2 = Array(-1); // -> Uncaught RangeError: Invalid array


length

console.log(testArray2.length);

En el ejemplo, hemos intentado crear dos arreglos usando el constructor (es decir, la función
predeterminada) Array . Si pasamos un argumento a esta función, se tratará como el tamaño
del arreglo recién creado. El primer arreglo ( testArray1 ) se crea sin ningún problema. Como
puedes adivinar, falla la creación del arreglo testArray2 con una longitud negativa.

5. Otros errores

Existen algunos tipos de errores más: EvalError , InternalError y URIError , pero son
bastante raros, regresaremos a ellos si es necesario.

La sentencia try ... catch


Como dijimos anteriormente, las excepciones interrumpen la ejecución del programa. La
instrucción try ... catch , que también mencionamos antes, te permite cambiar esta acción
predeterminada. El programa interrumpirá lo que está haciendo actualmente, pero no terminará
automáticamente. La sintaxis para try...catch se ve así:

try {

//codigo a probar

} catch (error) {

// código a ejecutar en caso de un error, que lanza una excepción

La premisa básica es simple: si tenemos un fragmento de código que posiblemente pueda salir mal,
podemos incluirlo en la cláusula try . JavaScript intentará ejecutar este código, y si ocurre algún error y
se lanza una excepción, se ejecutará el código dentro del bloque catch ; si el código try se ejecuta sin
errores, entonces se ignora el bloque catch . Después de ejecutar los comandos del bloque catch , el
programa continuará ejecutándose desde la primera instrucción fuera de la instrucción try ...
catch .

Toma en cuenta que la palabra clave catch va seguida de paréntesis que contienen el error de
parámetro. Este es un nombre de variable que contendrá información sobre el error que se detectó, y
puede tener cualquier nombre válido, excepto los nombres error , err o simplemente e , son de uso
común. En el caso de una excepción lanzada por JavaScript, el objeto de error contendrá información
sobre el tipo de error y se convierte en una cadena para ser registrada o procesada de cualquier forma
que necesite el desarrollador.

Entonces, modifiquemos el código que vimos anteriormente, y que sabemos con seguridad arroja
errores:

try {

let a = b;

} catch (error) {

console.log(error); // -> ReferenceError: b is not defined

console.log("Hemos manejado la excepción"); // -> Hemos manejado la


excepción

La sentencia que produce ReferenceError ahora está dentro del bloque try . El resultado es que
nuestro código ya no se detiene por errores. Y podemos reaccionar en el bloque catch. En este ejemplo,
registramos un mensaje sobre el error. El primer error que se arroje en el bloque try siempre se
detectará, la ejecución saltará al bloque catch y no habrá más errores en el bloque try el bloque será
atrapado. Lo importante es que después de salir del bloque catch , el programa seguirá funcionando con
normalidad (en nuestro caso escribirá "Hemos manejado la excepción" en la consola).

Toma en cuenta que try...catch no funcionará en un SyntaxError . Esto no debería ser una
sorpresa para ti. Como hemos dicho varias veces antes, si el motor JavaScript detecta un error de
sintaxis, no te permitirá ejecutar el programa. Si el programa no se ha ejecutado, es bastante difícil
imaginar que pueda reaccionar de alguna manera a lo que ha sucedido.

Tarea

Reescriba todos los ejemplos de este capítulo de tal manera que los errores sean capturados por una
sentencia try...catch .
Manejo de excepciones condicionales
A veces queremos poder reaccionar de manera diferente a tipos específicos de errores dentro
del bloque catch. Podemos hacer esto usando el operador instanceof. Hablaremos del
operador en sí más adelante, porque es un tema bastante complicado. Por ahora, es
suficiente saber cómo podemos usarlo al manejar errores. La sintaxis del
operador instanceof tiene este aspecto:

variable instanceof type

Si, por ejemplo, recibimos un error en el bloque catch (pasado como argumento de error),
podemos comprobar si es del tipo ReferenceError de la siguiente manera :

let result = error instanceof ReferenceError;

El operador instanceof devuelve un valor booleano, por lo que esta expresión


devolverá true si la variable de error contiene un tipo ReferenceError y false si no es así.
Podemos usar if...else o sentencias switch para luego ejecutar un código diferente en el
caso de diferentes errores si es necesario.

En este ejemplo, podemos ver cómo podemos reaccionar de una manera específica solo al
tipo de error seleccionado:

let a = -2;
try {
a = b;
} catch (error) {
if (error instanceof ReferenceError) {
console.log("Reference error, restablecer a a -2"); // ->
Reference error, restablecer a a -2
a = -2;
} else {
console.log("Otro error - " + error);
}
}
console.log(a); // -> -2

Es importante saber que cualquier variable que se declare usando la palabra clave let dentro
de un bloque try no es accesible en el bloque catch (ni en el bloque finally, que se
discutirá en un momento). Si no estás seguro de por qué es así, vuelve por un momento al
capítulo sobre declaraciones de variables y su alcance de visibilidad.

La sentencia finally
El último bloque opcional de la sentencia try es el bloque finally. Se puede usar con o sin
el bloque catch, y siempre se ejecuta después de los bloques try y catch,
independientemente de que se produzca algún error. La sintaxis para try ... finally se
ve así:

try {
// código a probar
} finally {
// esto siempre se ejecutará
}

Hagamos un pequeño experimento. Haremos una sustitución correcta a la variable a dentro


del bloque try.

let a = 10;
try {
a = 5;
} finally {
console.log(a); // -> 5
}
console.log(a); // -> 5

Desglosemos nuestro ejemplo tratando de referirnos a una variable inexistente, b. Como


puedes adivinar, esto generará un error de ReferenceError:

let a = 10;
try {
a = b; // ReferenceError
} finally {
console.log(a); // -> 10
}
console.log(a);

¿Qué está pasando esta vez? La excepción (ReferenceError) interrumpe el programa en el


bloque try. Debido a que el motor de JavaScript no puede encontrar el bloque catch,
inmediatamente salta al bloque finally, ejecutando su contenido y finalizando su trabajo.

El bloque finally también se puede usar junto con el bloque catch, ya que ambos son
opcionales, pero al menos uno de ellos es requerido por try, y si ninguno de ellos está
presente, se lanza un SyntaxError.

let a = 10;
try {
a = b; // ReferenceError
} catch (error) {
console.log("¡Un error!"); // -> ¡Un error!
} finally {
console.log("¡Finalmente!"); // -> ¡Finalmente!
}
console.log(a); // -> 10
En este caso, la excepción provoca un salto al bloque catch, luego al bloque finally.
Luego, el programa continúa funcionando fuera de la sentencia try...catch.

¿Por qué deberíamos usar un bloque finally?


Esta es una buena pregunta, especialmente porque podemos lograr casi el mismo resultado
simplemente escribiendo el código justo fuera de la instrucción try...catch, así:

let a = 10;
try {
a = b; // ReferenceError
} catch (error) {
console.log("¡Un error!");
}
console.log("¡Finalmente!");

Este código tendrá un resultado similar al del ejemplo anterior: registrará ¡Un error! y
luego ¡Finalmente!. Es cierto que en este sencillo ejemplo, ambos scripts se comportarán
igual, pero hay ligeras diferencias, y la más importante es que el bloque finally se ejecutará
incluso cuando se arroje un error desde el bloque catch.

let a = 10;
try {
a = b; // Primer ReferenceError
} catch (error) {
console.log(b); // Segundo ReferenceError

}
console.log("¡Finalmente!");

Ahora la última llamada a console.log nunca se ejecutará, ya que se lanza otro error (esta
vez no detectado) en el bloque catch. Esto no sucederá si usamos el bloque finally así:

let a = 10;
try {
a = b; // Primer ReferenceError
} catch (error) {
console.log(b); // Segundo ReferenceError

} finally {
console.log("¡Finalmente!");
}
Ahora se ejecutará la llamada console.log del bloque finally, aunque esto no cambia el
hecho de que la ejecución del programa se detendrá en este segundo ReferenceError, ya
que no se captura.

Los bloques try...catch...finally se pueden anidar, por lo que podemos usar un bloque
completo try...catch dentro de otro bloque try...catch. Esto es útil cuando esperamos
que ocurran múltiples errores y necesitamos manejarlos todos.

let a = 10;
try {
a = b; // Primer ReferenceError
} catch (error) {
try {
console.log(b); // Segundo ReferenceError
} catch {
console.log("¡Segundo catch!"); // -> ¡Segundo catch!
}
} finally {
console.log("¡Finalmente!"); // -> ¡Finalmente!
}

En este ejemplo, detectamos la excepción dentro del bloque catch colocando el código
dentro de otra instrucción try...catch.

La instrucción throw y los errores personalizados


Existen varias razones por las que puedes generar tus propias excepciones. La mayoría de
ellas son bastante complejos y no muy útiles en esta etapa de aprendizaje. La situación más
fácil de imaginar es cuando escribes una función propia, que debería señalar los datos
incorrectos que se le han pasado.

Para lanzar una excepción, usamos la instrucción throw. Le sigue cualquier valor que será
tratado como una excepción. Puede ser, por ejemplo, un número o uno de los objetos de error
preparados (por ejemplo, RangeError).

Una excepción que lancemos hará que el programa reaccione de la misma manera que las
excepciones de JavaScript originales (es decir, detendrá su ejecución). Es decir, a menos que
lo arrojemos dentro del bloque try para manejarlo. Veamos un ejemplo sencillo, sin tratar de
encontrarle ningún significado especial. Esta es solo una ilustración del uso de la
instrucción throw:

console.log("inicio"); // -> inicio


throw 100; // -> Uncaught 100
console.log("fin");

Una excepción no admitida (si el número 100 se puede llamar una excepción) hace que el
programa se detenga. La segunda instrucción console.log nunca se ejecuta.

Cerremos la instrucción throw dentro del bloque try:


console.log("inicio"); // -> inicio
try {
throw 100;
} catch (error) {
console.log(error); // -> 100
}
console.log("fin"); // -> fin

Esta vez, nuestra excepción es capturada y manejada en el bloque catch y no interrumpe la


ejecución posterior.

La sentencia throw y los errores personalizados -


continuación
Intentemos escribir algo un poco más sensato.

Soñamos con una función que nos permita contar factoriales (espero que todavía recuerdes lo que es un
factorial de tus lecciones de matemáticas; si tienes dudas, echa un vistazo rápido a Wikipedia para ver un
ejemplo). Lo escribiremos en una versión iterativa, es decir, usando un bucle. No será la solución más
elegante ni la más óptima, sino simple y legible.

function factorial(n) {
let result = 1;
for (; n > 1; n--) {
result = result * n;
}
return result;
}

console.log(factorial(3)); // -> 6
console.log(factorial(5)); // -> 120
console.log(factorial(8)); // -> 40320
console.log(factorial(20)); // -> 2432902008176640000
console.log(factorial(1000)); // -> Infinity

Digamos que estamos un poco asustados por los grandes números que devuelve nuestra función,
especialmente el valor Infinity, por lo que decidimos limitar el rango máximo de valores admitidos.
No aceptaremos argumentos mayores de 20.

function factorial(n) {
if (n > 20) {
throw new RangeError("Valor máximo 20");
}
let result = 1;
for (; n > 1; n--) {
result = result * n;
}
return result;
}

console.log(factorial(20)); // -> 2432902008176640000


console.log(factorial(1000)); // -> Uncaught RangeError: Valor máximo
20

La presencia de una instrucción condicional dentro de nuestra función es bastante obvia. También lo es
el uso de la instrucción throw. La construcción new RangeError("Valor máximo 20")
definitivamente necesita una explicación. Está un poco fuera del alcance de esta parte del curso, por lo
que intentaremos explicarlo de la manera más simple posible, centrándonos solo en su lado funcional.

Como mencionamos anteriormente, la instrucción throw puede tomar cualquier valor. Anteriormente,
usábamos un número simple, pero esta vez buscamos algo más complejo. Es un objeto, que es un tipo de
dato compuesto. Puede crear un nuevo objeto de muchas maneras, incluso mediante el uso del operador
new. Usando este operador, creamos un objeto de clase RangeError, que es un error predefinido que
discutimos hace un tiempo. El nuevo objeto es iniciado por la cadena Valor máximo 20. Y tal nuevo
objeto, del tipo RangeError, que contiene, entre otras cosas, la cadena que proporcionamos, será
lanzado si el parámetro n excede el valor permitido.

Resumen
Ocurrirán errores, pero cuando un desarrollador los acepta, puede escribir código que estará
listo para la mayoría de ellos. Es una buena práctica prepararse siempre para los problemas y
también ejecutar el código con la mayor frecuencia posible, ya que esto minimizará la cantidad
de código nuevo que potencialmente puede introducir nuevos errores. Trata de recordar
algunas cosas básicas:

 Mientras escribes y pruebas el programa, lee detenidamente todo lo que aparece en la


consola. La información de error suele ser bastante precisa, así que no intentes hacer
correcciones a ciegas.
 El hecho de que hayas eliminado todos los errores semánticos y de sintaxis no
significa que el programa ya sea 100 % correcto. Los errores lógicos solo pueden
ocurrir bajo ciertas circunstancias específicas.
 Anticipa y desconfía de los usuarios, su comportamiento y los datos que ingresan. No
puedes asumir que los datos que recibes siempre serán correctos.
 Consulte la documentación de JavaScript para conocer el comportamiento de los
operadores y las funciones en caso de errores. A veces se lanzarán excepciones, a
veces se devolverá un valor específico (recuerde los errores aritméticos). Sin
embargo, siempre trata de estar preparado para manejar errores.

Tareas

Tarea 1
Escribe tu propia función div que tomará dos argumentos de llamada y devolverá el resultado
de dividir el primer argumento entre el segundo. En JavaScript, el resultado de dividir entre
cero es el valor Infinity (o -Infinity, si intentamos dividir un número negativo). Cambia
esto. Si se pasa 0 como segundo argumento, tu función debería lanzar una
excepción RangeError con el mensaje apropiado. Prepara una llamada de prueba de la
función tanto para la división válida como para la división entre cero.

Ejemplo

function div(a, b) {

if (b == 0) {

throw new RangeError("No se puede dividir entre 0");

return a / b;

console.log(div(4, 2)); // -> 2

console.log(div(4, 0)); // -> Uncaught RangeError: No se puede dividir


entre

Tarea 2

Hemos declarado un arreglo de números:

let numbers = [10, 40, 0, 20, 50];

Escribe un programa que, en un bucle, divida el número 1000 entre elementos sucesivos del
arreglo de números, mostrando el resultado de cada división. Para dividir los números, usa la
función de la tarea anterior. Usa la sentencia try...catch para manejar una excepción
lanzada en el caso de la división entre cero. Si se detecta una excepción de este tipo, el
programa debe imprimir un mensaje apropiado (tomado de la excepción) y continuar su
operación (división por elementos sucesivos del arreglo).

Ejemplo

for (let i = 0; i < numbers.length; i++) {

let result;

try {

result = div(1000, numbers[i]);

} catch (e) {
result = e.message;

console.log(result);

 6.3.1.2 Probando y depurando tu código



 Probando y depurando tu código
 Como ya hemos escrito, ocurren errores en los programas. Es completamente normal. Al
principio, la mayoría de las veces cometerás errores debido a la falta de conocimiento del
lenguaje de programación (por ejemplo, errores de sintaxis). Serán fáciles de corregir, porque el
intérprete los detectará y, por lo general, también sugerirá lo que está mal. Los errores lógicos,
sin embargo, son un problema diferente. Como mostramos anteriormente, el intérprete no tiene
forma de detectarlos, por lo que tenemos que buscar por nosotros mismos la respuesta a la
pregunta: ¿por qué el programa no funciona como asumimos?
 Veamos un ejemplo sencillo:
 function average(a, b) {
 return a + b / 2;
 }

 console.log(average(2, 10)); // -> 7 se espera: 6
 console.log(average(5, 5)); // -> 7.5 se espera: 5

 Vemos una función que calcula el promedio de dos números y devuelve el resultado. La función
parece simple: suma dos números dados y los divide entre 2. Este código tiene una sintaxis
válida, no tiene problemas formales y esperamos que los resultados de las dos llamadas en el
ejemplo sean 6 y 5. Pero cuando ejecutamos este código, los resultados son muy diferentes.
¿Puedes ver dónde está el error?
 return a + b / 2;

 Esto no funciona como se esperaba (dividir la suma de dos números entre 2) debido al orden de
las operaciones. La división b / 2 se calcula primero, luego se le suma a, por lo que este
código es el mismo que este:
 return a + (b / 2);

 Para producir el resultado que esperamos, el código debería verse así:


 return (a + b) / 2

 Este es un buen ejemplo de un error lógico. El código en sí es perfectamente válido, nada de lo


que quejarse desde el punto de vista de JavaScript. Pero la función no devuelve los valores que
pretendía el programador. La mayoría de las veces, este tipo de errores son los más difíciles de
encontrar si el código no se prueba correctamente.


 Aquí tenemos otro ejemplo donde el error no es tan obvio:
 function largest(a, b, c) {
 if (a > b && a > c) {
 return a;
 } else if (b > a && b > c) {
 return b;
 } else {
 return c;
 }
 }

 Vemos una función que debería devolver el mayor de tres números. La idea de cómo resolver
este problema es simple: cuando la variable a es mayor que b y c, a es el número más grande.
Si este no es el caso, entonces si b es mayor que a y c, b es el mayor número. Si ninguno es
cierto, eso significa que c es el número más grande. Toma tu tiempo y trata de detectar la falla
en esta lógica. Como sugerencia, intenta llamar a la función con estos conjuntos de parámetros:
 console.log(largest(1, 1, 2)); // -> 2
 console.log(largest(1, 2, 3)); // -> 3
 console.log(largest(3, 2, 1)); // -> 3
 console.log(largest(2, 2, 1)); // -> 1

 ¿Puedes ahora detectar el error en función de cuándo ocurre?


 Si el primer y el segundo número son iguales, la función devuelve incorrectamente el tercer
valor. Esto se debe a que cuando a y b son iguales, tanto a > b y b > a no son ciertas. Este
error es mucho más difícil de encontrar, ya que este código hace lo que debería la mayor parte
del tiempo, y solo en casos específicos devuelve valores incorrectos. Cuando se encuentra, el
problema es bastante fácil de solucionar, ya que solo necesitamos cambiar el operador mayor
que a un operador mayor o igual que dentro de nuestra instrucción if.
 Depuración
 Para llevarse a cabo de manera eficiente, la depuración requiere herramientas, y si
nuestro código se ejecuta en el navegador, es casi seguro que ya tenemos todas las
herramientas necesarias disponibles. Para comprobar si nuestro navegador admite
esta funcionalidad, simplemente podemos intentar ejecutar este código con la consola
del desarrollador.
 console.log("Antes del depurador");
 debugger;
 console.log("Despues del depurador");

 Si el depurador está presente, la consola mostrará solo el mensaje "Antes del


depurador" y, según el navegador instalado, deberíamos ver información sobre la
ejecución del código detenida, pausada o en modo de depuración. El segundo
mensaje no se muestra porque la sentencia debugger funciona como un punto de
interrupción en la ejecución del código. Entonces, cada vez que JavaScript encuentra
la sentencia debugger, verifica si el depurador está presente y, de ser así, la ejecución
del código se detiene en ese punto exacto. Por supuesto, esto no es útil en sí mismo,
pero es solo el comienzo de las características del depurador.

Ejecución del programa paso a paso


Una de las características principales del depurador es su capacidad para ejecutar código paso a paso.
Esto significa que podemos detener la ejecución del programa en cualquier lugar usando una
instrucción debugger y luego continuar la ejecución una instrucción a la vez.

Esto es realmente útil cuando sospechamos que el comportamiento o la lógica del programa es
defectuosa y el código va a una rama de ejecución incorrecta (va a la sentencia if incorrecta, etc.). En
este modo, podemos ver cada línea que se ejecuta y cada línea que no. Podemos ver fácilmente si la
lógica en las sentencias de control de flujo es válida o no.

Ya sabemos que la sentencia debugger, cuando JavaScript la encuentre, detendrá la ejecución del
código en ese lugar. Dependiendo del navegador que estemos usando, los botones de control de flujo
pueden verse diferentes y pueden estar ubicados en diferentes lugares. En general, todos los navegadores
modernos admiten las siguientes opciones para controlar la ejecución del script en modo de depuración:

 Reanudar/Continuar. Esto reanudará la ejecución del script de forma normal y se usa cuando
hemos verificado lo que queríamos verificar y ahora queremos continuar con la ejecución del
script.

 Step Into. Esto ejecuta la siguiente instrucción en el código solamente, y lo pausa de nuevo, y
lo usamos cuando queremos analizar el código en detalle, o verificar qué ruta exacta toma la
ejecución cuando ocurre una bifurcación compleja debido a las sentencias if. ..else u otra
lógica complicada. Si la siguiente instrucción es una llamada de función, usar Step Into saltará
dentro del código de esta función.

 Step Over. Esto funciona como Step Into, excepto que si se usa cuando la siguiente instrucción
es una llamada de función, el código no saltará al código de función, pero se ejecutará toda la
función, y el programa se pausará nuevamente después de salir de esta función. Esto se usa a
menudo cuando la siguiente instrucción es una llamada a una función en la que no sabemos si
tendrá algún impacto, o simplemente no estamos interesados en buscar.

 Step Out. Esto nos permite salir inmediatamente de una función en la que el código está en
pausa.

Intentemos practicar algunas acciones básicas que se pueden realizar con el depurador. El programa
JavaScript que vamos a depurar debe reescribirse en su entorno de desarrollo local (por alguna razón, la
depuración es más legible si no usamos la plataforma OpenEDG en estos ejercicios). ¿Recuerdas cómo
puedes hacer algo como esto? Al comienzo del curso, ejecutamos nuestro código abriendo un archivo
HTML simple en el navegador, que incluía una referencia al archivo JavaScript que se ejecutaría (el
capítulo titulado "El Programa ¡Hola, Mundo!").

Preparación del entorno y un ejemplo


Crea dos archivos en cualquier editor de código (describimos cómo configurar el entorno local en el
capítulo "Herramientas de Desarrollo"): index.html y main.js. En el archivo index.html,
coloca el código para esta página web HTML muy simple:

<!DOCTYPE html>
<html>
<head>
<script src="main.js"></script>
</head>
<body>
<p>Sitio de Prueba</p>
</body>
</html>

Guarde el archivo en tu disco local, preferiblemente en un directorio vacío recién creado. En el mismo
directorio, guarda el archivo main.js (al cual, como habrás notado, se hace referencia en el
código index.html), colocando en su interior el siguiente código:

function outer() {
let name = "outer";
let str = inner();
return str;
}

function inner() {
let name = "inner";
return "¡Hola!";
}

console.log("Antes de llamar a outer()");


console.log(outer());
console.log("Despues de llamar a outer()");

En el navegador que estás utilizando, abre una nueva pestaña y carga el archivo index.html.
Dependiendo de tu navegador y sistema, puedes usar el menú del programa o el atajo de teclado
apropiado (en Linux y Windows: Ctrl + O, en macOS: CMD + O). Si todo se ha hecho correctamente,
verás este texto en la pestaña: "Sitio de Prueba".

Ahora necesitamos iniciar las herramientas de desarrollo. Discutimos cómo ejecutarlos en diferentes
sistemas y navegadores en el capítulo titulado "Herramientas de Desarrollo". Por ejemplo, en los
navegadores Chrome y Firefox, en Windows y Linux, usamos la combinación de teclas: Ctrl + Shift + I.
En el resto de este ejercicio, nos limitaremos a discutir cómo funciona el depurador usando los
navegadores Chrome y Firefox como ejemplos.

Selecciona Console o Consola en la pestaña Herramientas para Desarrolladores. Recarga la página


(combinación de teclas Ctrl + R o CMD + R). Los siguientes mensajes deberían aparecer en la consola:

Antes de llamar a outer()


¡Hola!
Despues de llamar a outer()

Este es el resultado de los métodos console.log del programa escrito en el archivo main.js. Si todo ha
funcionado hasta ahora, estamos listos para comenzar a jugar con la depuración.
Uso de la sentencia debugger
Probemos la sentencia debugger en la práctica. Colócala en el código main.js antes de
llamar a la función outer. Entonces, las últimas líneas del archivo main.js ahora deberían
verse así:

console.log("Antes de llamar a outer()");


debugger;
console.log(outer());
console.log("Despues de llamar a outer()");

No olvides guardar el archivo modificado. Vuelve a tu navegador y vuelve a cargar la página.


¿Qué ha sucedido? En primer lugar, en las Herramientas para Desarrolladores, la pestaña
seleccionada ha cambiado: en Chrome, será Sources o Fuentes, en Firefox Debugger o
Depurador. La sentencia debugger hace que el programa detenga su ejecución en la línea
donde lo colocamos y espere nuestra decisión. En la pestaña, entre otra información, deberías
ver el código de nuestro programa, con la línea en la que se ha detenido la ejecución
claramente resaltada.

En la vista Sources / Debugger o Fuentes / Depurador, también tenemos la opción de usar la


consola (no tenemos que cambiar a la pestaña Console). Intenta presionar la tecla Esc varias
veces. Observa que la consola aparecerá y desaparecerá en la parte inferior de la pestaña.
Para trabajos futuros, déjalo visible. Dado que solo se ejecuta un console.log antes de que
el programa se detenga, solo deberías ver lo siguiente en la consola:

Antes de llamar a outer()


Reanudar la ejecución
A la derecha de la pestaña, busca el botón Reanudar o Resume (el icono del triángulo girado a la
derecha: reproducir). Si pasas el mouse sobre este botón, debería aparecer una información sobre
herramientas para que puedas asegurarte de que es el botón correcto. Pulsa este botón o utilizar el
método abreviado de teclado F8. Como resultado, el programa seguirá adelante y, sin detenerse más, se
ejecutará hasta el final por sí mismo. La consola ahora debería mostrar la información completa
generada por el programa:

Antes de llamar a outer()


¡Hola!
Despues de llamar a outer()

Usar Reanudar o Resume no necesariamente hace que el programa se ejecute por completo. Podemos
indicar dónde debe detenerse nuevamente. Vuelve a cargar la página. Observa que el depurador muestra
números de línea a la izquierda del código. Haz clic en el número 15, que indica la última línea de
nuestro código. Así es como establecemos el punto de interrupción (la línea se resaltará). Haz clic en el
número de línea nuevamente si deseas eliminar el punto de interrupción (no lo elimines todavía). Si
ahora hacemos clic en el botón Reanudar o Resume (o usamos F8), el programa continuará y se detendrá
en el punto de interrupción. Como resultado, la consola mostrará el siguiente texto:

Antes de llamar a outer()


Solo al hacer clic en Reanudar o Resume de nuevo, el programa se ejecutará por completo y la consola
mostrará:

Antes de llamar a outer()


¡Hola!
Despues de llamar a outer()

Para ser honesto, al depurar código, rara vez usamos la sentencia debugger. La mayoría de las veces, en
el lugar donde el programa debe detenerse, simplemente lo indicamos usando puntos de interrupción
establecidos directamente en las Herramientas para Desarrolladores. Antes de seguir trabajando, elimina
los puntos de interrupción (haciendo clic en los números de línea apropiados).
Cómo lidiar sin la sentencia debugger
Nuevamente, modifica el programa guardado en main.js, esta vez eliminando la línea que contiene el
comando debugger. Guarda los cambios, vuelve a tu navegador y vuelva a cargar la página. Obviamente,
el programa se ha ejecutado hasta el final, pero ahora sabemos cómo detenerlo. Establece dos puntos de
interrupción, uno en console.log("Antes de llamar a outer()"); el otro
en console.log("Después de llamar a outer()"); (ahora estas deberían ser las líneas 12 y
14 respectivamente). Vuelve a cargar la página. El programa debe detenerse en el primer punto de
interrupción. Al hacer clic en Reanudar o Resume, el programa reanudará la ejecución y se detendrá en
el segundo punto de interrupción.
Otro clic en Reanudar o Resume hará que el programa se ejecute hasta el final.

6.3.1.9 Step over



 Step over
 Además de saltar entre puntos de interrupción sucesivos con Reanudar o Resume, tenemos la
posibilidad de realizar una ejecución real paso a paso (es decir, llamar a las instrucciones de
nuestro programa una por una). Hay un pequeño problema aquí. Si una instrucción es una
llamada a una función, ¿debería el depurador entrar en la función y ejecutar las instrucciones
dentro de ella paso a paso, o tratarla como un todo indivisible y simplemente ejecutarla? Por
supuesto, no hay una respuesta correcta y todo dependerá de la situación específica y de lo que
queramos lograr. Es por eso que los depuradores distinguen entre dos modos de ejecución por
pasos: Step Into (tratando la función como un conjunto de instrucciones, que queremos ejecutar
por separado) y Step Over (tratando la llamada de función como algo indivisible).
 Elimina el segundo punto de interrupción (de la última línea del código) y vuelve a cargar la
página. Localiza el botón Step Over (a la derecha de Reanudar o Resume, la flecha que forma
un arco sobre el punto). Presiónelo: el resaltado en el código debe moverse a la siguiente línea
después del punto de interrupción. Al mismo tiempo, la consola mostrará el efecto de la
instrucción que acabas de realizar.

 Presiona Step Over dos veces más (como alternativa, usa el atajo F10) observando los cambios
en la consola y el resaltado del código.
 Step into
 Veamos cuál es la diferencia entre Step Over y Step Into en la práctica. Deja la configuración
del punto de interrupción sin cambios y vuelve a cargar la página. Primero ejecuta Step
Over (presiona el botón o el atajo F10). Luego, cuando nos detengamos en la
línea console.log(outer()), ejecuta Step Into.
 ¿Qué sucede? Esta vez, el depurador trata la función outer como un conjunto de instrucciones,
salta dentro de ella y se establece en su primera línea. Usando Step Into, avanza más en la
función inner y deténte en la línea return "¡Hola!".

Call Stack
Este es un buen momento para echar un vistazo a otro elemento del depurador: el Call Stack o Pila de
Llamadas. En una ventana con ese nombre, podemos ver en qué función nos encontramos actualmente
(en nuestro caso, inner ). Es más, allí veremos todas las funciones que están actualmente activas. La
pila de llamadas es importante en el caso de funciones anidadas, como en nuestro ejemplo. Usando Step
Into, llamamos a la función outer en el programa principal, ingresamos y llamamos a la función inner. Si
nos detenemos dentro de la función inner, entonces las funciones activas serán: inner y outer (creando
una pila). En la parte inferior de la pila, veremos la función principal (no tiene nombre, y en Firefox está
marcada como (global) y en Chrome (anónima)). Este es el lugar desde donde se llama a la función
outer.

Nos detenemos en la línea 9, dentro de la función inner, en el comando return "¡Hola!". Así que estamos
en el contexto de la función inner en este punto. En la consola en la parte inferior de la pantalla, escribe
el comando:

console.log(name); // -> inner

Como resultado de su ejecución, debería mostrarse el nombre "inner" (es decir, el contenido del nombre
de la variable local de la función inner). Si hace clic en el nombre de la función outer en la pila de
llamadas, serás llevado al contexto de esa función (toma en cuenta que la selección de la línea actual ha
cambiado). Intenta llamar al mismo comando nuevamente en la consola:

console.log(name); // -> outer

Esta vez deberíamos ver "outer". Estamos en el contexto de la función outer, que tiene su propia variable
local llamada name. Esta variable contiene la palabra "outer". Vuelve a hacer clic en el nombre de la
función inner en la Call Stack o Pila de Llamadas para volver a cambiar el contexto. Toma en cuenta
que a pesar del cambio de contexto, la ejecución del programa aún se detiene exactamente en el mismo
lugar.

Visualización y modificación de variables

Durante la ejecución paso a paso, tenemos libre acceso a las variables de nuestro programa, las cuales
son visibles en el contexto en el que nos encontramos actualmente. Como acabamos de ver, usando el
método console.log podemos escribir los valores de tales variables. También podemos modificarlos sin
ningún problema.

Volvamos al contexto de la función inner. Ejecuta la siguiente secuencia de comandos en la consola:

console.log(name); // -> inner

name = "new name";


console.log(name); // -> new name

Como puede ver, hemos modificado el valor de la variable local name, que se encuentra en la función
inner. Si continuamos la ejecución del programa (Step o Resume), el programa utilizará este nuevo
valor.

Visualización y modificación de variables - continuación


Encima de la ventana Call Stack hay otra ventana llamada Watch (o Watch expressions). Nos permite
ver y modificar las variables sin usar la consola. En esta ventana, podemos encontrar el botón +, que
luego de presionarlo, podemos ingresar el nombre de la variable cuyos cambios de valor queremos
rastrear. Para cambiar el valor actual de una variable, basta con hacer doble clic en la ventana de
observación sobre la variable observada e ingresar el nuevo valor. Recuerda que durante las llamadas a
funciones o en bloques de código, el alcance de la visibilidad de las variables puede variar, así que no te
sorprendas si los valores de las variables locales no son visibles en el contexto global.
Step out
Durante la depuración, puede ser útil usar una opción más. En el panel con los botones Resume, Step
Over o Step Into, también encontrarás un botón Step Out. Reanuda el funcionamiento del programa
ejecutando comandos sucesivos hasta que se sale de la función actual a la función desde la que se llamó.

Elimina todos los puntos de interrupción y establece uno nuevo en la línea 8, dentro de la función inner.
Vuelve a cargar el programa y la ejecución debería detenerse en la línea que hemos marcado. Presionar
el botón Step Out ejecutará el resto de las instrucciones en la función inner y se detendrá en la primera
línea después de llamarla (dentro de la función outer). Sencillo, ¿verdad?

El ejemplo que usamos realmente no requiere depuración. Es solo para demostrarte las funciones básicas
del depurador. Las necesitarás si el programa que vas a escribir se comporta de manera inconsistente en
relación con tus expectativas.

A veces no es posible localizar el problema inmediatamente y es necesario rastrear el funcionamiento del


programa fragmento a fragmento, preferiblemente usando una operación paso a paso. Luego, podemos
verificar cómo cambian los valores de las variables en los pasos posteriores, qué comandos se ejecutan o
si las condiciones o los bucles se han construido correctamente o no.
La capacidad de usar un depurador es esencial para todo programador.

Medición del tiempo de ejecución del código

El requisito básico que imponemos a los programas es, por supuesto, que funcionen correctamente. Su
funcionamiento debe ser predecible y coherente con nuestras suposiciones, y los resultados que arrojan
deben ser correctos. Sin embargo, la eficiencia del programa a menudo también es importante. A veces,
el mismo efecto se puede lograr de varias maneras, por lo que vale la pena elegir la que funcionará no
solo correctamente sino también rápidamente.

En los ejemplos discutidos hasta ahora en el curso, la velocidad ha tenido una importancia marginal. Los
programas eran simples, realizaban operaciones sin complicaciones y estas operaciones solían ser muy
pocas. Sin embargo, el aspecto de la velocidad de ejecución del código es bastante importante. Se ve
afectado por muchos elementos, como la elección de un algoritmo óptimo para resolver un problema
determinado, la selección de funciones apropiadas o evitar acciones redundantes.

Una de las formas más sencillas de medir la velocidad del programa es utilizar los
métodos console.time y console.timeEnd , que nos permiten realizar una medición precisa del
tiempo entre dos lugares especificados en nuestro código, y mostrar el resultado en la consola. Por
supuesto, existen muchas más herramientas avanzadas, que pueden ayudarnos durante la optimización
de nuestro código, pero vale la pena conocer estos métodos simples, que en muchos casos son
suficientes para analizar el rendimiento del programa.

Medición del tiempo de ejecución del código -


continuación
Supongamos que queremos calcular el valor aproximado del número pi. Hay muchos métodos que
permiten esto, uno de los cuales es utilizar la fórmula de Leibniz:

Que se puede ampliar en la serie:

El valor de pi calculado de esta manera es aproximado, pero es más preciso cuanto más larga es la serie
(es decir, cuanto mayor es el valor de k que usamos). Por supuesto, una mayor precisión implicará un
mayor número de operaciones a realizar y, por lo tanto, afectará el tiempo de ejecución del programa.
Veamos cómo se vería un programa de ejemplo escrito en JavaScript, que nos permitiría realizar tales
cálculos:
let part = 0;
for (let k = 0; k < 10000000; k++) {
part = part + ((-1) ** k) / (2 * k + 1);
}
let pi = part * 4;
console.log(pi); // -> 3.1415925535897915

La parte variable contendrá un resultado parcial, que se modificará en cada iteración del bucle for. El
bucle se ejecutará diez millones de veces (es decir, k tomará valores de 0 a 999999). La parte que más
tiempo consumirá será la ejecución del bucle for, porque en cada iteración se realizan operaciones como
sumar, multiplicar, dividir y exponenciación.

Veamos cuánto tiempo tarda el programa en ejecutar este fragmento de código. Para ello utilizaremos
los métodos console.time y console.timeEnd.

let part = 0;
console.time('Leibniz');
for (let k = 0; k < 10000000; k++) {
part = part + ((-1) ** k) / (2 * k + 1);
}
console.timeEnd('Leibniz'); // -> Leibniz: 456.60498046875 ms
let pi = part * 4;
console.log(pi); // -> 3.1415925535897915

Con console.time, indicamos dónde iniciar la medición del tiempo, mientras que
con console.timeEnd finalizamos la medición, y el resultado se muestra en la consola en este punto
(obviamente el resultado que obtengas será diferente al del ejemplo). El tiempo se da en milisegundos.
En las llamadas de los métodos console.time y console.timeEnd, podemos especificar una
cadena (en el ejemplo es 'Leibnitz') que identificará nuestro cronómetro en caso de que usemos
muchos de ellos en nuestro programa.

Medición del tiempo de ejecución del código -


continuación
Supongamos que queremos calcular el valor aproximado del número pi. Hay muchos métodos que
permiten esto, uno de los cuales es utilizar la fórmula de Leibniz:

Que se puede ampliar en la serie:


El valor de pi calculado de esta manera es aproximado, pero es más preciso cuanto más larga es la serie
(es decir, cuanto mayor es el valor de k que usamos). Por supuesto, una mayor precisión implicará un
mayor número de operaciones a realizar y, por lo tanto, afectará el tiempo de ejecución del programa.
Veamos cómo se vería un programa de ejemplo escrito en JavaScript, que nos permitiría realizar tales
cálculos:

let part = 0;
for (let k = 0; k < 10000000; k++) {
part = part + ((-1) ** k) / (2 * k + 1);
}
let pi = part * 4;
console.log(pi); // -> 3.1415925535897915

La parte variable contendrá un resultado parcial, que se modificará en cada iteración del bucle for. El
bucle se ejecutará diez millones de veces (es decir, k tomará valores de 0 a 999999). La parte que más
tiempo consumirá será la ejecución del bucle for, porque en cada iteración se realizan operaciones como
sumar, multiplicar, dividir y exponenciación.

Veamos cuánto tiempo tarda el programa en ejecutar este fragmento de código. Para ello utilizaremos
los métodos console.time y console.timeEnd.

let part = 0;
console.time('Leibniz');
for (let k = 0; k < 10000000; k++) {
part = part + ((-1) ** k) / (2 * k + 1);
}
console.timeEnd('Leibniz'); // -> Leibniz: 456.60498046875 ms
let pi = part * 4;
console.log(pi); // -> 3.1415925535897915

Con console.time, indicamos dónde iniciar la medición del tiempo, mientras que
con console.timeEnd finalizamos la medición, y el resultado se muestra en la consola en este punto
(obviamente el resultado que obtengas será diferente al del ejemplo). El tiempo se da en milisegundos.
En las llamadas de los métodos console.time y console.timeEnd, podemos especificar una
cadena (en el ejemplo es 'Leibnitz') que identificará nuestro cronómetro en caso de que usemos
muchos de ellos en nuestro programa.

Medición del tiempo de ejecución del código -


continuación
Veamos dentro del bucle for. En cada iteración, el número -1 se eleva a la potencia de k. La
exponenciación es una operación que requiere bastante tiempo, por lo que podemos
sospechar que afecta fuertemente la velocidad de nuestro programa (especialmente cuando lo
hacemos diez millones de veces). Si la base de la exponenciación es el número -1, siempre
obtendremos como resultado -1 o 1, dependiendo de si el exponente es par o impar. En este
caso, podemos reemplazar la exponenciación con una instrucción condicional que verifica si k
es par (divisible por 2) o impar y devuelva 1 o -1 respectivamente.

let part = 0;
console.time('Leibniz');
for(let k = 0; k < 10000000; k++) {
part = part + (k % 2 ? -1 : 1) / (2 * k + 1);
}
console.timeEnd('Leibniz'); // -> Leibniz: 175.5458984375 ms
let pi = part * 4;
console.log(pi);

Como puedes ver, ¡incluso un cambio tan pequeño nos permite más del doble de la velocidad
del programa! El uso de los métodos console.time y console.timeEnd nos permite
analizar el rendimiento de nuestro código. Si tenemos la impresión de que algo funciona
demasiado lento, pero no sabemos qué fragmento de código es el responsable, podemos
realizar mediciones, localizar el problema y, opcionalmente, intentar optimizar el código. Como
decíamos antes, hay muchas herramientas que también nos ayudan en esto. Algunos de ellos
están integrados en las herramientas de desarrollo integradas con el navegador, pero a
menudo los métodos que se muestran son suficientes para realizar pruebas básicas.

Intenta probar ambas soluciones en tu entorno local y analiza qué diferencias en los tiempos
obtienes.

Resumen
La capacidad de utilizar las herramientas de desarrollo, incluido el debugger, es muy
importante en el trabajo de un programador. Permite, entre otros, eliminar eficazmente errores
lógicos en nuestro código, así como optimizarlo.

Sin el uso de la ejecución paso a paso, a menudo no podremos descubrir las razones por las
que nuestro programa no funciona como se esperaba. La posibilidad de detener la ejecución
de una determinada instrucción, comprobando el estado actual de las variables o rastreando
la secuencia de llamadas a funciones anidadas, puede resultar crucial a la hora de probar y
arreglar nuestro programa..

Tareas

Tarea 1

Ejecuta el siguiente código:

let end = 2;
for(let i=1; i<=end; i++) {
console.log(i);
}

Debería dar como salida los números 1 y 2 en la consola. Usa el debugger para hacer que el
programa genere los números 1, 2, 3, 4 y 5. No modifiques el código del programa. Usa solo
puntos de interrupción y la opción de modificar variables.

Tarea 2

Use el debugger para comprender por qué el resultado final registrado es igual a cero cuando
en cada iteración del for, aumenta un valor de bucle la variable result. Utiliza Watch para
realizar un seguimiento de los cambios en las variables seleccionadas.

let counter = 0;
let maxValue = 10;
let result = 1;

debugger;
for (counter = 0; counter < maxValue; counter++) {
console.log(result);
result *= maxValue - counter - 1;
}

console.log("Resultado final: ", result);

Tarea 3

Ejecuta el siguiente código:

function max(array) {
let maxValue = array[1];
for(let i=1; i<array.length; i++) {
if(array[i] > maxValue) {
maxValue = array[i];
}
}
return maxValue;
}

console.log( max([1, 4, 6, 2])); // -> 6


console.log( max([10, 4, 6, 2])); // -> 6
La función max debe devolver el número más grande del arreglo dado como argumento.
Como puedes ver, en el segundo caso funciona incorrectamente: en lugar del
valor 10 obtenemos 6. Usando el depurador, rastrea la ejecución de la función max paso a
paso. Observa los valores de las variables i y maxValue. Localiza el problema y corrige el
código para que funcione correctamente.

También podría gustarte