Descargar como docx, pdf o txt
Descargar como docx, pdf o txt
Está en la página 1de 211

<Fundamentos de JavaScript 1 (JSE)

Módulo 1
Introducción a JavaScript y la Programación Informática

Después de completar el Módulo 1, el estudiante:

 Comprenderá los conceptos fundamentales de programación, tales como: la


interpretación y el intérprete, la compilación y el compilador, la programación del
lado del cliente y al lado del servidor.
 Tendrá los conocimientos básicos sobre cómo configurar y utilizar el entorno de
programación básico (en línea o local).
 Adquirirá habilidades que le permitirán ejecutar su primer programa JavaScript en
el lado del cliente (tanto como un elemento incrustado en la página HTML como
directamente en la consola del navegador).

Fundamentos de JavaScript 1 (JSE):


Módulo 1

Sección 1
Acerca de JavaScript

Temas en la sección:

 ¿Cómo comunicarse con la computadora?


 ¿Qué es JS?
 JS – Ventajas y Limitaciones
 ¿Dónde se usa JS hoy?

Introducción
¿Quieres aprender a programar en JavaScript? ¡Genial!

Este curso será un viaje bastante largo, pero si llegas hasta el final, podrás leer,
comprender y, por supuesto, escribir aplicaciones y programas en JavaScript. Estas nuevas
habilidades pueden ayudarte en tu trabajo actual o permitirte alcanzar nuevas
oportunidades profesionales en un mercado de TI en constante crecimiento.
Comencemos esta aventura sin más preámbulos y descubramos qué es JavaScript.

Cómo comunicarse con la computadora


Comencemos con algo obvio: las computadoras están en todas partes. Es casi seguro que
estás leyendo este curso en una computadora; tu teléfono es una computadora.
Encontrarás computadoras en televisores y otros electrodomésticos. Estamos rodeados
de computadoras. Las computadoras se utilizan en la ciencia, la medicina, los bancos y los
negocios. Sospechamos que hoy en día sería difícil encontrar algún ámbito de la vida en el
que las computadoras no participen activamente.

Usamos computadoras principalmente porque son capaces de realizar ciertas tareas


incomparablemente más rápido y con mayor precisión que las personas. Sin embargo, las
computadoras no pueden adivinar qué tipo de tareas tenemos en mente o cómo
ayudarnos a realizarlas. Tenemos que decirles eso. ¿Cómo? Es mejor hacerlo de forma
similar a cuando se pasa información a otras personas, es decir, utilizando un lenguaje
que sea comprensible para ambas partes. Usando dicho lenguaje, escribimos un
programa, una solución formal a nuestro problema, que puede ser ejecutada por la
computadora.

Desafortunadamente, un lenguaje que es directamente entendible por una computadora


sería absolutamente ilegible para un ser humano normal. Sería una secuencia de
instrucciones extrañas, escritas en forma numérica, refiriéndose a componentes
informáticos que ni siquiera sabíamos que existían (y hablando francamente, no
necesariamente tenemos que saberlo). Esta forma de comunicación, que data de los
comienzos de la informática, hoy en día se usa muy raramente y solo en situaciones muy
específicas.

Entonces, para ayudar en la comunicación con las computadoras, se inventaron los


lenguajes de programación que son algo similares a los lenguajes naturales (es
decir, los que se usan para la comunicación entre personas). Hay miles de lenguajes
de programación, y difieren en propósito (aparte de los lenguajes de propósito
general, hay muchos especializados, por ejemplo, lenguajes diseñados solo para
cálculos estadísticos), nivel de abstracción (en términos simples: cuanto mayor sea
el nivel del lenguaje , menos necesitamos saber sobre el hardware en el que se
ejecuta el programa), facilidad de uso, efectividad de los programas escritos en
ellos, etc. 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.

¿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.

¡Ahora, vamos a empezar!

Fundamentos de JavaScript 1 (JSE):


Módulo 1

Sección 2
Configuración del entorno de programación

Temas en esta sección:

 Herramientas de desarrollo
 Entorno de desarrollo en línea
 Entorno de desarrollo local (editor de código, intérprete, depurador)

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.

En este momento, existen dos opciones. Puedes instalar todas las herramientas
necesarias en tu máquina y trabajar en el entorno local. Este es el enfoque preferido, ya
que así es como se ve en proyectos comerciales reales la mayor parte del tiempo.
También puedes personalizar todo para satisfacer tus necesidades.

Todo el código que verás en este curso se probó en entornos locales y en línea, por lo que
ambas opciones son válidas. Finalmente, podemos pasar a la elección de las
herramientas.

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).

 La apertura de los entornos de desarrollo web es tanto una bendición como una
maldición. Podemos elegir entre cientos de componentes, a partir de los cuales
podemos crear el entorno más cómodo para nosotros.
 Sin embargo, su cantidad, más los cambios dinámicos de herramientas
particulares o incluso las tendencias entre los programadores hacen que sea difícil
mantenerse al día con todo lo que sucede dentro de estos entornos.

 Pero para nosotros, este es un problema para el futuro lejano.

 Por ahora, necesitamos el trío mínimo: un editor de


código, intérprete y depurador

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.


Sublime Text

[Windows, macOS, Linux]

Editor de código rápido y fácil de usar con muchas funciones avanzadas, como edición de varias
líneas, búsqueda rápida y otras. Hay una versión de prueba disponible, pero para uso a largo
plazo, se debe comprar una licencia ya sea para un uso privado y/o comercial.

 Notepad++

[Windows]
Editor de código y texto gratuito y ligero. El programa es pequeño y rápido, admite docenas de
lenguajes de programación y se puede ampliar con complementos (plugins). Puede ser viejo y
feo, pero sigue siendo muy útil.

Existen muchos otros editores de código, tanto gratuitos como de paga, y puedes usar el
que prefieras. Muchos desarrolladores usan, entre otras cosas, editores de consola,
incluido el legendario vim. Los editores de consola no se ejecutan en un entorno gráfico,
sino en una consola de texto. Sin embargo, solo puedes buscar tales soluciones si las
tareas que vas a hacer resultan ser demasiado simples y quieres hacer tu vida un poco
más difícil.

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.

Recuerda actualizar regularmente tu navegador elegido y usar la última versión. Esto es


especialmente importante cuando se trabaja con JavaScript. El lenguaje cambia
constantemente, con nuevas características y mecanismos que se agregan. Es posible que
tu navegador favorito, pero algo anticuado, no admita ciertas funciones del lenguaje. Tu
navegador ahora es una herramienta, así que intenta mantenerlo en buen estado
actualizándolo periódicamente.

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).

Fundamentos de JavaScript 1 (JSE):


Módulo 1
Sección 3
¡Hola, Mundo!

Temas en esta sección:

 Algunas palabras sobre HTML


 ¿Cómo puedes ejecutar tu código JavaScript?
 Ejecutando el código directamente en la consola

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.

¿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.

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.

Resumen
Nuestro primer programa se lanzó en un entorno en línea al principio. Este entorno nos
permite ocultar ciertos detalles que no son importantes para nosotros en esta etapa del
curso. Todos los ejercicios y ejemplos que discutiremos deben realizarse en este entorno.
Sin embargo, de vez en cuando, sería bueno que intentaras hacer el ejemplo elegido
también en el entorno local. Está mucho más cerca de lo que realmente se usa en el
trabajo de un desarrollador web. Ejecutar código JavaScript en el entorno local puede
parecer un poco engorroso al principio, pero afortunadamente esta es solo una primera
impresión. Recuerda, para probar instrucciones simples, solo necesitas usar la consola
con una página vacía (por ejemplo, about:blank). Si deseas probar un código un poco más
grande, es mejor crear un archivo html que se refiera al archivo que contiene nuestro
código JavaScript usando la etiqueta  <script> .

Fundamentos de JavaScript (JSE)


Módulo 2
Variables, Tipos de Datos, Conversión de Tipos de Datos y Comentarios

Después de completar el Módulo 2, el estudiante:

 Tendrá los conocimientos y las habilidades para trabajar con variables (es decir:
nombrar, declarar, inicializar y modificar sus valores).
 Comprenderá conceptos como el alcance, los bloques de código, el sombreado y el
hoising.
 Conocerá las propiedades básicas de los tipos de datos primitivos, como boolean,
number, bigint, undefined, null, y será capaz de utilizarlos.
 Estará familiarizado con las propiedades básicas del tipo de dato primitivo string o
cadena, incluidos los literales de cadena: comillas simples o dobles, el carácter de
escape, la interpolación de cadenas, propiedades y métodos básicos.
 Conocerá las propiedades básicas de tipos de datos complejos como Array y
Object (tratados como registros) y será capaz de usarlos en la práctica.

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.
LABORATORIO

Tiempo Estimado
15-30 minutos

Nivel de Dificultad
Fácil

Objetivos
Familiarizar al estudiante con:

 variables (es decir, nombrar, declarar, inicializar y modificar su valor)

Escenario
Nuestra tarea será crear una lista de contactos. Inicialmente, la lista será bastante simple:
solo escribiremos tres personas utilizando los datos que se muestran en la tabla a
continuación. En el resto del curso, volverá a este script y lo ampliará sistemáticamente
con nuevas funciones, utilizando los elementos de JavaScript recién aprendidos.

Nombre Teléfono Correo


Maxwell Wright (0191) 719 6495 [email protected]
Raja Villarreal 0866 398 2895 [email protected]
Helen Richards 0800 1111 [email protected]

Declara e inicializa las variables donde almacenarás toda la información (nueve variables
en total). Muestra en la consola información sobre el primer y último contacto en el
formulario: nombre/teléfono/correo.
Fundamentos de JavaScript 1 (JSE):

Modulo 2 - Sección 2
Tipos de datos y sus conversiones: Parte 1

Temas en esta sección:

 Tipos de datos en JS
 Tipos de datos primitivos: Boolean (booleano)
 Tipos de datos primitivos: Number (numérico)
 Tipos de datos primitivos: BigInt (entero largo)
 Tipos de datos primitivos: String (cadena)
 Tipos de datos primitivos: undefined (indefinido)
 Tipos de datos primitivos: Symbol (símbolo)
 Tipos de datos primitivos: null (nulo)
 Conversión de tipos de datos: Función de construcción primitiva
 Conversión de tipos de datos: Conversiones primitivas
 Conversión a String (cadena)
 Conversión a Number (numérico)
 Conversión a Boolean (booleano)
 Conversión a BigInt (entero largo)
 Conversiones implicitas

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.

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  (0x10) , 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 by zero

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.

Los métodos de cadena y las propiedades comúnmente utilizados (es decir, valores con
nombre relacionados con el objeto) son:

 length : propiedad que devuelve el número de caracteres en una cadena.

 charAt(Index) : método que devuelve el carácter en la posición "Index" en las


cadenas (los índices comienzan desde 0).
 slice(beginIndex, [opcional] endIndex) : método que devuelve una nueva
cadena que se crea a partir de los caracteres entre  beginIndex  (incluido)
y  endIndex  (excluido); si se omite  endIndex , entonces la nueva cadena es
desde  beginIndex  hasta el final de la cadena.

 split(separator, [opcional] limit) : método que divide la cadena en


subcadenas cada vez que se encuentra un separador en esa cadena y devuelve
una arreglo de esas subcadenas (diremos algunas palabras sobre arreglos en un
momento), mientras que un límite opcional  limit  limita el número de subcadenas
añadidas a la lista.

let str = "java script language";


console.log(str.length); // -> 20
console.log('test'.length); // -> 4

console.log(str.charAt(0)); // -> 'j'


console.log('abc'.charAt(1)); // -> 'b'

console.log(str.slice(0, 4)); // -> 'java'


console.log('test'.slice(1, 3)); // -> 'es'

console.log(str.split(' ')); // -> ['java', 'script', 'language']


console.log('192.168.1.1'.split('.')); // -> ['192', '168', '1',
'1']

Para entenderlo correctamente, es necesario ejecutar ejemplos de datos del tipo String.
No tengas miedo de experimentar cambiando los datos en los ejemplos, agregando
nuevas variables o mostrando información adicional en la consola.

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

Datos Primitivos

 Escribe un fragmento de código que creará variables y las inicializará con valores
Boolean, Number, BigInt, String y tipos undefined utilizando (siempre que sea
posible) literales y funciones constructoras.
 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 , por ejemplo, en el siguiente
formato: 1000 [número].
 Realiza una cadena de conversiones: crea 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?
 Intenta agregar dos valores del mismo tipo y verifica el tipo de resultado. Pruébalo
para todos los tipos de datos primitivos.
 Intenta sumar dos valores de diferentes tipos y verifica los resultados.
 Intenta modificar la línea const  str1 = 42 + "1";  para obtener el
resultado  43  (sin eliminar las comillas alrededor del 1)

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.

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 .

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?

Tarea 4

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

Tarea 5

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

Tarea 6
Intenta modificar la línea  const str1 = 42 + "1";  para obtener el resultado  43  (sin
eliminar las comillas alrededor del  1  ).

Fundamentos de JavaScript 1 (JSE):


Módulo 2

Sección 3
Tipos de datos y su conversión: Parte 2

Temas en esta sección:

 Tipos de datos complejos: objetos


 Tipos de datos complejos: arreglos
 Arreglos: la propiedad length
 Arreglos: el método indexOf
 Arreglos: el método push
 Arreglos: el método unshift
 Arreglos: el método pop
 Arreglos: el método shift
 Arreglos: el método reverse
 Arreglos: el método slice
 Arreglos: el método concat

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"
};

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

¿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
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"]
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.

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.

Tareas

Objetos

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.
Declara un objeto vacío y guárdalo en la variable person. Usando la notación de punto,
agrega los campos  firstName  y  lastName  al objeto ingresando tus datos como valores.
Intenta mostrar los campos individuales en la consola.

Arreglos

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:

1. Speaking JavaScript, Axel Rauschmayer, 460.


2. Programming JavaScript Applications, Eric Elliott, 254.
3. Understanding ECMAScript 6, Nicholas C. Zakas, 352.
 Crea un arreglo de tres objetos que representen los libros. Cada objeto debe tener
las siguientes propiedades: título, autor, páginas.
 Hemos agregado un nuevo libro a nuestra colección: Learning JavaScript Design
Patterns, por Addy Osmani, 254 páginas. Usa el método apropiado para adjuntar
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.
 Utiliza el comando slice para copiar los dos últimos libros al nuevo arreglo.
 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.
 Muestra la suma de las páginas de todos los libros de la colección.

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.

Fundamentos de JavaScript 1 (JSE):


Módulo 2

Sección 4
Comentarios

Temas en la sección:

 Comentarios de una sola línea


 Comentarios de varias líneas
 Documentación
 Alternar código
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.

Fundamentos de JavaScript 1 (JSE)


Módulo 3
Operadores e Interacción con el Usuario

Después de completar el Módulo 3, el estudiante:

 Sabrá qué son los operadores y cómo clasificarlos (por tipo de operando, por
número de operandos, etc).
 Será capaz de utilizar operadores de asignación, aritméticos, lógicos y de
comparación en la práctica.
 Comprenderá la función del operador condicional y los operadores typeof,
instanceof y delete.
 Comprenderá cuál es la precedencia y la asociatividad de los operadores básicos y
será capaz de influir en ellos por medio de la agrupación de paréntesis.
 Podrá realizar una comunicación bidireccional básica con el usuario del programa
utilizando los cuadros de diálogo de alerta, confirmación y solicitud.

Fundamentos de JavaScript 1 (JSE):


Módulo 3

Sección 1
Operadores de asignación, aritméticos y lógicos
Temas en esta sección:

 ¿Qué son los operadores?


 Operadores de asignación
 Operadores aritméticos
 Operadores aritméticos - operadores de asignación compuesta
 Operadores logicos
 Operadores logicos - operadores de asignación compuesta

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.

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

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);

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);

Fundamentos de JavaScript 1 (JSE):


Módulo 3

Sección 2
Cadenas, comparación y otros operadores JS

Temas en esta sección:

 Operadores de cadenas: concatenación y asignación compuesta


 Operadores de comparación
 Otros operadores JS (typeof, instanceof, delete, y ternary)
 Prioridad de los operadores

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,  false  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 Operador Asociatividad Símbolo
14 Agrupamiento n/a (…)
13 Acceso al campo ⇒ ….…
12 Llamada de función ⇒ …(…)
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 …
Igualdad ⇒ … == …
Desigualdad ⇒ … != …
5
Igualdad estricta ⇒ … === …
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.

Fundamentos de JavaScript 1 (JSE):


Módulo 3

Sección 3
Interacción con el usuario

Temas en esta sección:

 ¿Cómo interactuar con el usuario en JavaScript?


 Cuadros de diálogo: alerta
 Cuadros de diálogo: confirmación
 Cuadros de diálogo: aviso
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.

LABORATORIO

Tiempo estimado
15-30 minutos

Nivel de dificultad
Fácil

Objetivos
Familiarizar al alumno con:

 La interacción con el usuario (cuadros de diálogo: alerta, confirmación y aviso)

Escenario
Volvamos a nuestra lista de contactos. Después de algunos ajustes recientes (es decir, el
emplear un arreglo y objetos), en realidad comienza a parecerse a una lista. Sin embargo,
quedaba un problema bastante grave. El cambio de datos en la lista, como agregar un
nuevo contacto, debe proporcionarse de inmediato en el código del programa. ¿Qué
sucede si queremos darle al usuario la posibilidad de ingresar los datos del contacto
agregado mientras se ejecuta el programa? Modifica el programa para agregar, al final de
la lista, no el contacto, que se da en el código, sino el que el usuario dará durante la
ejecución del programa. Usa el método  prompt  para hacer esto. Al final, muestra el
primer y último contacto de la lista.

Fundamentos de JavaScript 1 (JSE):


Módulo 4
Control de Flujo: Ejecución Condicional y Bucles

Después de completar el Módulo 4, el estudiante:

 Podrá forzar la ejecución condicional de un grupo de sentencias de código (tomar


decisiones y bifurcar el flujo) usando los comandos if-else y switch.
 Podrá obligar a un grupo de sentencias de código a repetirse en un bucle usando
los comandos for, while y do-while, usando condiciones dependientes e
independientes en el número de iteraciones.
 Comprenderá y será capaz de usar las instrucciones break y continue dentro de un
bucle.
 Podrá usar la instrucción for-in para iterar sobre las propiedades de un objeto.
 Podrá usar la instrucción for-of para recorrer los elementos de un arreglo.

Fundamentos de JavaScript 1 (JSE):


Módulo 4

Sección 1
Ejecución condicional

Temas en esta sección:

 ¿Qué es la ejecución condicional?


 La instrucción if
 La instrucción if-else
 La instrucción if-else-if
 El operador condicional
 La instrucción switch-case

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
sentencia 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  .

Tarea 2

Reescribe el código anterior, reemplazando el  if  con un operador condicional ternario.

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 .

Fundamentos de JavaScript 1 (JSE):


Módulo 4

Sección 2
Bucles

Temas en esta sección:

 ¿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

¿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++;
}

El 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.

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).

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.

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

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]}`);
}

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.

LABORATORIO

Tiempo Estimado
15-30 minutos

Nivel de dificultad
Fácil

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 .

Fundamentos de JavaScript 1 (JSE):


Módulo 5
Funciones

Después de completar el Módulo 5, el estudiante:

 Podrá declarar y mandar llamar funciones.


 Sabrá cómo pasar argumentos a una función y devolver el resultado de su
operación.
 Comprenderá el concepto de una variable local y el efecto de sombrear variables
con los mismos nombres dentro de una función.
 Sabrá que una función en JS es un miembro de primera clase y podrá aprovechar
esto declarando funciones usando expresiones de función y pasando funciones
como argumentos a otras funciones.
 Comprenderá el concepto de recursividad en el contexto de funciones y será capaz
de resolver problemas de programación simples usándola.
 Tendrá una comprensión básica de la función callback y podrá usarla de forma
asíncrona junto con los métodos setTimeout y setInterval.
 Tendrá una comprensión clara de la notación de funciones de flecha y podrá
escribir funciones alternativas como declaraciones regulares, expresiones de
función y funciones de flecha.

Fundamentos de JavaScript 1 (JSE):


Módulo 5

Sección 1
Funciones: Parte 1

Temas en esta sección:

 ¿Qué son las funciones?


 Declaración de funciones
 Invocación de funciones
 Variables locales
 La sentencia return
 Parámetros de funciones
 Sombreado
 Validación de parámetros

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.

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

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.

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

Fundamentos de JavaScript 1 (JSE):


Módulo 5

Sección 2
Funciones: Parte 2

Temas en esta sección:

 Recursividad
 Funciones como miembros de primera clase
 Expresiones de funciones
 Callbacks síncronas
 Callbacks asíncronas
 Funciones arrow

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.

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

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...

let interna = function() {


    console.log('inner 1');
    }
   
    let externa = function(callback) {
    console.log('outer 1');
    let timerId = setInterval(callback, 1000) /*ms*/;
    console.log('outer 2');
   
    setTimeout(function(){
        clearInterval(timerId);
    }, 5500); //se eliminará el intervalo después de 5.5 segundos
    }
   
    console.log('test 1');
    externa(interna);
    console.log('test 2');

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
Normalmente, sin embargo, las llamadas a funciones asincrónicas están relacionadas con
situaciones ligeramente diferentes. Están determinados por eventos que no están
relacionados con los temporizadores, sino que se generan fuera del programa. Como
hemos dicho antes, pueden ser, por ejemplo, acciones realizadas por el usuario, como
hacer clic con el ratón en un elemento de la interfaz de una página. Los escenarios de este
tipo se discutirán en detalle en la siguiente parte del curso, dedicada a la integración de
JavaScript del lado del cliente con sitios web y el DOM (Modelo de Objetos de Documento).

Sin embargo, intentaremos analizar un ejemplo que ilustre la situación de una forma algo
simplificada.

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.

Tareas
Tareas 1

Los arreglos en JavaScript tienen disponible un método  sort  que, como puedes suponer,
te permite ordenar sus elementos. Este método toma como argumento una función que
comparará dos elementos del arreglo. La función debe devolver cero si consideramos que
los argumentos son iguales, un valor menor que cero si consideramos que el primero es
menor que el segundo y un valor mayor que cero en caso contrario. Mira el ejemplo:

let numbers = [50, 10, 40, 30, 20];


function compareNumbers(a, b) {
let retVal = 0;
if (a < b) {
retVal = -1;
} else if(a > b) {
retVal = 1;
}
return retVal;
}
let sorted = numbers.sort(compareNumbers);
console.log(sorted); // [10, 20, 30, 40, 50]
A. Intenta modificar el código anterior para que sea lo más corto posible. Sugerencias:

 Usar una función anónima.


 Usar una función arrow o flecha
 Considera omitir la sentencia  if .

B. Luego modifica la función para que los elementos se ordenen en orden descendente,
no en orden ascendente como en el ejemplo.

Tarea 2

Escribe tres funciones con los nombres  add ,  sub  y  mult , que tomarán dos argumentos
numéricos. Las funciones son para verificar si los argumentos dados son enteros
(emplea  Number.isInteger ). Si no, devuelven  NaN , de lo contrario, devuelven el
resultado de la suma, la resta o la multiplicación, respectivamente. Las funciones deben
declararse mediante una instrucción de función.

Ejemplo de su uso y resultados esperados:

console.log(add(12, 10)); // -> 22


console.log(mult(12, 10.1)); // -> NaN

Tarea 3

Reescribe las funciones de la tarea anterior usando una expresión de función arrow o
flecha, tratando de escribirlas en la forma más corta posible.

Ejemplo de su uso y resultados esperados:

console.log(sub(12, 10)); // -> 2


console.log(mult(10, 10.1)); // -> NaN

Tarea 4

Escriba una función  action  que tomará la función callback como su primer argumento y
los otros dos argumentos como números. Como función callback, podrá pasar una de las
tres funciones de la tarea anterior. La función  action  llamará a la función callback que se
le pasó y devolverá el resultado obtenido. La función callback aceptará el segundo y el
tercer argumento de la invocación.

Ejemplo de su uso y resultados esperados:

console.log(action(add, 12, 10)); // -> 22


console.log(action(sub, 12, 10)); // -> 2
console.log(action(mult, 10, 10.1)); // -> NaN
Tarea 5

Escribe un programa que imprima (en la consola) números enteros consecutivos 10 veces,
en intervalos de dos segundos (comienza con el número 1). Utiliza las
funciones  setInterval ,  clearInterval  y  setTimeout .

Ejemplo de su uso y resultados esperados:

1
2
3
4
5
6
7
8
9
10

Tarea 6

Escribe una función que calcule el n-ésimo elemento de la sucesión de Fibonacci. Esta
secuencia se define mediante una fórmula:

Entonces, cada elemento de la secuencia (excepto los dos primeros) es la suma de los dos
anteriores. Por ejemplo: F1 = 1, F2 = F1 + F0 = 1, F3 = F2 + F1 = 2 y F6 = F5 + F4 = 8. La función
debe usar recursividad. En la definición, usa una expresión de función (almacena una
función anónima en una variable).

Ejemplo de su uso y resultados esperados:

console.log(fibbRec(4)); // -> 3
console.log(fibbRec(7)); // -> 13

Tarea 7
Reescribe la función de la Tarea 6 usando una expresión de función arrow o flecha, pero
intenta acortar tu código tanto como sea posible (emplea operadores condicionales y
trata de no usar variables adicionales que no sean el parámetro  n ).

Tarea 8

Escribe una versión iterativa de la función de la Tarea 6 (usa el bucle  for ). Declara la
función usando una instrucción de función.

LABORATORIO

Tiempo Estimado
30-45 minutos

Nivel de Dificultad
Medio

Objetivos
Familiarizar al estudiante con:

 Conceptos básicos de funciones (qué son las funciones, declaración de funciones,


llamada o invocación de funciones, variables locales, la sentencia return, los
parámetros de las funciones, sombreado).
 Funciones como miembros de primera clase (expresiones de funciones, pasar una
función como parámetro, funciones callback).
 Funciones arrow o flecha (declaración e invocación).
 Recursividad (concepto básico).

Escenario
Nuestro programa ha crecido bastante, por lo que es un poco difícil de leer. Es
especialmente visible en la instrucción switch, donde se incluye la mayor parte de la
lógica. Trata de organizar el código de tu programa usando funciones. Define y llama a
tres funciones en los lugares apropiados:

 showContact : la función debe tomar dos argumentos; el primero es la lista de


contactos y el segundo es el número de índice del contacto a mostrar; dentro de la
función, verifica si se pasan los argumentos correctos, es decir, si los contactos son
un arreglo (utiliza la construcción  instanceofArray  para esto).
 showAllContacts : la función debe tomar un argumento, la lista de contactos;
dentro de la función, verifica si el argumento dado es un arreglo.
 addNewContact : la función debe tomar cuatro argumentos, una lista de contactos
y los datos del nuevo contacto, es decir: nombre, teléfono y número; antes de
agregar un nuevo contacto, verifica si el argumento pasado es un arreglo y si los
datos del nuevo contacto tienen algún valor.

LABORATORIO

Tiempo Estimado
30-60 minutos

Nivel de Dificultad
Medio

Objetivos
Familiarizar al estudiante con:

 Conceptos básicos de funciones (qué son las funciones, declaración de funciones,


llamada o invocación de funciones, variables locales, la sentencia return, los
parámetros de las funciones, sombreado).
 Funciones como miembros de primera clase (expresiones de funciones, pasar una
función como parámetro, funciones callback).
 Funciones arrow o flecha (declaración e invocación).
 Recursividad (concepto básico).

Escenario
Usaremos las funciones para agregar un elemento más de funcionalidad. Los arreglos
tienen un método  sort  que nos permite ordenar sus elementos. A este método, le
pasamos una función que debe comparar dos elementos del arreglo y decidir cuál es más
pequeño y cuál es más grande. Si el primer elemento es menor, la función devuelve un
valor menor que cero, si son iguales devuelve cero, y si el primero es mayor, devuelve un
valor mayor que cero. Por ejemplo, el arreglo:
let numbers = [10, 50, 40, 20];

se puede ordenar de manera ascendente:

numbers.sort(function (a, b) {
let retVal = 0;
if (a > b) {
retVal = 1;
} else {
retVal = -1;
}
return retVal;
});

o simplemente:

numbers.sort((a, b) => a - b);

Da al usuario la opción de seleccionar una acción  sort  de la lista. Cuando se selecciona
esta opción, el usuario debería poder especificar si desea ordenar los contactos por
nombre, teléfono o correo electrónico.

Fudamentos de JavaScript 1 (JSE):


Módulo 6
Errores, Excepciones, Depuración y Solución de Problemas

Después de completar el Módulo 6, el estudiante:

 Obtendrá una comprensión de las diferencias entre errores sintácticos,


semánticos y lógicos.
 Entenderá el concepto de una excepción y distinguirá entre las excepciones
básicas generadas por JS cuando ocurre un error: SyntaxError, ReferenceError,
TypeError y RangeError.
 Tendrá la capacidad de manejar excepciones usando la instrucción try-catch-final.
 Podrá generar sus propias excepciones usando la instrucción throw.
 Tendrá las habilidades para usar el depurador para el análisis básico de su propio
código, que incluye: ejecución paso a paso, visualización y modificación de
variables, y medición del tiempo de ejecución del código.
Fudamentos de JavaScript 1 (JSE):
Módulo 6

Sección 1
Errores y Excepciones: Parte 1

Temas en esta sección:

 Errores: el pan de cada día del programador


 Lenguajes naturales y errores de comunicación
 Errores vs excepciones
 Errores sin excepciones
 Confianza limitada

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.

¿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.

Fundamentos de JavaScript 1 (JSE):


Módulo 6

Sección 2
Errores y Excepciones: Parte 2

Temas en esta sección:

 Tipos de errores: SyntaxError


 Tipos de errores: ReferenceError
 Tipos de errores: TypeError
 Tipos de errores: RangeError
 La sentencia try catch
 Manejo de excepciones condicionales
 La sentencia finally
 La instrucción throw y los errores personalizados

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.

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).

Fundamentos de JavaScript 1 (JSE):


Módulo 6

Sección 3
Depuración de Código y Resolución de Problemas

Temas de esta sección:

 ¿Qué es la depuración?
 Ejecución paso a paso
 Preparación del entorno
 La sentencia debugger
 La opción de reanudar
 Depuración de código sin la sentencia debugger
 La opción step over
 La opción step into
 La opción call stack
 Ver y modificar variables
 La opción step out
 Midiendo el tiempo de ejecución del 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()


Probemos ahora algunos escenarios simples.

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.

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
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