Fundamentos de JavaScript
Fundamentos de JavaScript
Módulo 1
Introducción a JavaScript y la Programación Informática
Sección 1
Acerca de JavaScript
Temas en la sección:
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.
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.
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.
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.
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.
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).
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.
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 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.
Pero comencemos ahora con JavaScript, que, debido a su sintaxis flexible y simple, es
perfecto para aprender como primer lenguaje.
Sección 2
Configuración del entorno de programació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).
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.
Entre los programadores de JavaScript, los más populares son los siguientes:
JSFiddle
CodePen
JsBin
Plunker
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.
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.
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.
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
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
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.
Nuestro curso trata sobre los fundamentos de JavaScript, es decir, aquellos elementos del
lenguaje que serán igualmente útiles en soluciones móviles, del lado del cliente y del lado
del servidor. Así podemos practicarlos en cualquier entorno, utilizando cualquier
intérprete. La forma más fácil de hacer esto es limitarse a un navegador web.
Como hemos dicho anteriormente, prácticamente todos los navegadores tienen motores
(o intérpretes) JavaScript integrados, pero recomendamos encarecidamente
usar Chrome de Google, o FireFox de Mozilla . Ambos son conocidos por su eficiencia y
herramientas avanzadas integradas para desarrolladores web (ese eres tú). Están
disponibles para Windows, macOS y Linux.
Depurador
Los programas de computadora son bestias complicadas, miles o incluso millones de
líneas de código (pero tranquilo, comenzaremos con solo unas pocas). Con tal
complejidad y tamaño, es imposible producir código sin errores. Algunos tipos de errores,
especialmente los lógicos (formalmente, el programa está escrito correctamente, pero
probablemente inventamos la solución incorrecta al problema), solo se pueden encontrar
mientras el programa se está ejecutando y, a menudo, solo en circunstancias especiales.
Es realmente difícil averiguar qué sucede exactamente dentro de un programa que se
ejecuta a la velocidad del rayo, y para esos problemas existen los depuradores.
El inspector, que nos permitirá, por ejemplo, analizar los elementos HTML
individuales de un sitio web abierto.
En el próximo capítulo, volveremos a este tema y aprenderemos algunas cosas más sobre
estas útiles herramientas.
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).
console.log("¡Hola, Mundo!");
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.
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!
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>
</body>
Documento HTML
<!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.
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.
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.
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í.
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.
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:
<!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.
<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.
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> .
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.
Sección 1
Variables
¿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.
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.
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 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
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.
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).
"use strict";
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:
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.
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.
¿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.
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
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
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.
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).
function testFunction() {
console.log("Hola");
console.log("Mundo");
}
Comencemos:
Hola
Mundi
y otra vez:
Hola
Mundo
y una vez más:
Hola
Mundo
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).
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.
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ó?
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:
¿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.
function testFunction() {
var counter = 200;
console.log(counter);
}
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.
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.
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).
var weight;
var height = 180;
console.log(height); // -> 180
console.log(weight); // -> undefined
weight = 70;
console.log(weight); // -> 70
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:
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.
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
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
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).
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).
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.
"undefined"
"object"
"boolean"
"number"
"bigint"
"string"
"symbol"
"function"
salida
Esta lista nos muestra aproximadamente qué tipos de datos trataremos en JavaScript.
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.
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.
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.
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
let a = 1 / 0;
let b = -Infinity;
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.
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).
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.
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:
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.
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).
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).
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.
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.
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.
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.
Los métodos de cadena y las propiedades comúnmente utilizados (es decir, valores con
nombre relacionados con el objeto) son:
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
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
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 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.
console.log(str); // ->
console.log(num); // -> 0
console.log(bool); // -> false
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.
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 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 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(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
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(true)); // -> 1n
console.log(BigInt("11")); // -> 11n
console.log(BigInt("0x11")); // -> 17n
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):
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
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
Tarea 6
Intenta modificar la línea const str1 = 42 + "1"; para obtener el resultado 43 (sin
eliminar las comillas alrededor del 1 ).
Sección 3
Tipos de datos y su conversión: Parte 2
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 = {
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.
¿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.
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).
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.age); // -> 66
user1.age = 67;
console.log(user1.age); // -> 67
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.
days[0] = "Sunday";
console.log(days[0]); // -> Sunday
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";
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.
¿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]"
};
let users =[
{
name: "Calvin",
surname: "Hart",
age: 66,
email: "[email protected]"
},
{
name: "Mateus",
surname: "Pinto",
age: 21,
email: "[email protected]"
}
];
users[2] = {
name: "Irene",
surname: "Purnell",
age: 32,
email: "[email protected]"
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.
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
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.
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.
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.
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.
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 n4 = names.slice(-1);
console.log(n4); // -> ["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.
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.
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
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].
Sección 4
Comentarios
Temas en la sección:
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.
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.
/*
Este es un bloque
de comentarios y puede
abarcar varias líneas
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.
// dividir a entre b
let result = a / b;
Documentación
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.
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.
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.
Sección 1
Operadores de asignación, aritméticos y lógicos
Temas en esta sección:
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).
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.
const x = 5;
const y = 2;
Ambos operadores convierten los operandos al tipo Number, mientras que el operador
de menos (negativo) lo niega.
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.
console.log(n1++);
es interpretada como:
console.log(n1);
n1 = n1 + 1;
console.log(++n1);
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.
x += 100;
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:
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:
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:
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:
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;
let nr = 0;
let year = 1970;
let name = "Alice";
let empty = "";
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).
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.
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
let b = false;
console.log(b); // -> false
b ||= true;
console.log(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):
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);
Sección 2
Cadenas, comparación y otros operadores JS
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.
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:
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).
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).
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 .
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
Si deseas refrescar tus conocimientos sobre este operador, vuelve a la sección sobre tipos
de datos.
Durante este curso, la utilidad de este operador se limita a probar si una variable contiene
un arreglo.
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
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.
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
a OP1 ( b OP2 c)
a OP1 b OP2 c
(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 ⇐ … *= …
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;
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;
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;
console.log(a); // -> 80
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.
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.
Sección 3
Interacción con el usuario
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.
alert("¡Hola, Mundo!")
window.alert("¡Hola, Mundo!, por segunda ocasión");
alert(4 * 7);
alert(true);
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.
console.log(message);
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.
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:
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.
Sección 1
Ejecución condicional
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
if (condición) {
bloque de código
}
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.
if (isUserReady)
console.log("¡Usuario listo!");
alert("¡Usuario listo!");
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:
console.log(shippingCost);
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 (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.
if (condición) {
condición - código verdadero
} else {
condición - código falso
}
Usando la nueva sintaxis, podemos reescribir nuestro ejemplo
anterior:
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.
if (condición_1) {
code
} else if (condición_2) {
code
} else if (condición_3) {
code
} else {
code
}
Para resumir lo que está pasando, podemos diseccionar cada caso por separado:
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:
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.
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.
Sin embargo, tendría más sentido guardar el mismo fragmento de código de la siguiente
forma:
Es posible tener código más largo como operandos, pero es mejor usar sentencias if, ya
que es mucho más claro.
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.
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
Tarea 2
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 .
Sección 2
Bucles
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.
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.
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
}
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).
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.
while (!isOver) {
isOver = !confirm(`[${counter++}] ¿Continuar en el bucle?`);
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:
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:
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:
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 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.
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.
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:
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.
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í:
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.
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:
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:
La versión de este programa que usa el bucle for ... of se verá ligeramente diferente:
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:
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 }
];
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]"
};
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
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.
let i = 0;
// Un bucle infinito
while (true){
console.log(i);
i++;
if (i >= 5) {
break;
}
}
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.
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.
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:
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.
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.
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!");
}
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
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.
Tarea 6
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.
Sección 1
Funciones: Parte 1
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.
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.
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.
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
}
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;
}
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
Entonces el código:
function showName() {
console.log(name);
}
function showName() {
console.log(name);
}
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.
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.
function showMsg() {
console.log("mensaje 1");
return;
console.log("mensaje 2");
}
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.
function getTrue() {
return 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;
}
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.
Veamos una función simple que suma dos valores. Lo llamaremos add.
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 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 .
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.
También intenta ejecutar y analizar un ejemplo más simple, en el que puede sombrear
variables con parámetros y variables locales.
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
Sección 2
Funciones: Parte 2
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.
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;
}
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:
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.
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.
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.
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.
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;
}
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
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;
}
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.
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
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.
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.
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.
window.addEventListener("click", function() {
console.log("¡hizo clic!");
});
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.
o simplificado aún más (la función tiene solo una sentencia, cuyo valor regresa):
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:
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:
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.
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.
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.
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 .
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).
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:
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:
LABORATORIO
Tiempo Estimado
30-60 minutos
Nivel de Dificultad
Medio
Objetivos
Familiarizar al estudiante con:
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];
numbers.sort(function (a, b) {
let retVal = 0;
if (a > b) {
retVal = 1;
} else {
retVal = -1;
}
return retVal;
});
o simplemente:
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.
Sección 1
Errores y Excepciones: Parte 1
Errores y excepciones
Es muy importante que te prepares para esta simple verdad:
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".
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.
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:
¿Qué sucede si reemplazamos una palabra por un error tipográfico con una que existe en
nuestro diccionario?
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.
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:
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:
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 .
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:
Sección 2
Errores y Excepciones: Parte 2
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");
}
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).
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.
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.
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.
try {
//codigo a probar
} catch (error) {
// código a ejecutar en caso de un error, que lanza una excepción
}
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.
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 .
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
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á
}
let a = 10;
try {
a = 5;
} finally {
console.log(a); // -> 5
}
console.log(a); // -> 5
let a = 10;
try {
a = b; // ReferenceError
} finally {
console.log(a); // -> 10
}
console.log(a);
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 .
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 .
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 :
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.
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;
}
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:
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:
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).
Sección 3
Depuración de Código y Resolución de Problemas
¿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
function average(a, b) {
return a + b / 2;
}
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);
return (a + b) / 2
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:
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.
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!").
<!DOCTYPE html>
<html>
<head>
<script src="main.js"></script>
</head>
<body>
<p>Sitio de Prueba</p>
</body>
</html>
function outer() {
let name = "outer";
let str = inner();
return str;
}
function inner() {
let name = "inner";
return "¡Hola!";
}
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".
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:
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).
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.
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.
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.
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.
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.
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.
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.
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
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;
}
function max(array) {
let maxValue = array[1];
for(let i=1; i<array.length; i++) {
if(array[i] > maxValue) {
maxValue = array[i];
}
}
return maxValue;
}
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.