Guía Completa de JavaScript Vol. I
Guía Completa de JavaScript Vol. I
VOL. I
Ariel Fuggini
[Link]
ISBN: 978-1-64921-207-8
ÍNDICE
Introducción
¿Por qué JavaScript?
JavaScript vs. Otros Lenguajes
El Estándar ECMAScript
Requerimientos Previos
Herramientas Útiles
Herramientas Adicionales
Terminología y Sintaxis
Literales
Expresiones y Sentencias
Uso de Punto y Coma
Comentarios
Tipos de Datos
Primitivos
Características de los Tipos Primitivos
Objetos
Operadores
Operadores aritméticos
Operadores Relacionales
Operadores de Comparación
Operadores de asignación
Operadores Lógicos
Operador Condicional Ternario
Operadores Unarios
Operador Coma
Variables
¡No es una caja!
Asignación e Igualdad
Tipado en JavaScript
Conocer el Tipo de Valor
Ámbito de las Variables (Scope)
Nombrar Variables
Control de Flujo
Sentencia de bloque
Sentencia condicional
Sentencias de iteración
Sentencia switch
Sentencia break
Sentencia continue
Funciones
Parámetros y Argumentos
Booleanos
Valores Truthy y Falsy
Números
Number()
Métodos de Number
NaN
Infinity
Math
Strings
String()
Propiedades y Métodos de String
Objetos
Literales de Objeto
Objetos anidados
Métodos de Objeto
Objetos Globales o Nativos
¿Todo en JavaScript son Objetos?
Arrays
Uso Práctico de Arrays
Métodos de Array
Coerción
Coerción Implícita
Coerción Explícita
Temporizadores
setTimeout()
setInterval()
Manipulando el DOM
Selectores
Insertando Contenido
Crear Nuevos Elementos
Manipulando Atributos
Manipulando Clases
ECMAScript 2015
let y const
Función Flecha
Plantillas de Cadena de Texto
Propiedades Abreviadas de Objeto
Desestructuración
Operador de Propagación
Parámetro Resto
Parámetros por Defecto
Sobre el Autor
INTRODUCCIÓN
Tiene la característica de ser mucho menos estricto que otros lenguajes, y esto lo hace más
amigable con quienes lo adopten como su primer lenguaje de programación, debido a que
posee mecanismos internos con los que permite que un programa funcione en casos en los
que otros lenguajes arrojarían errores.
Por otra parte, cada vez más sitios web hacen uso extensivo de JavaScript, y ya no solamente
para agregarle detalles atractivos. En los últimos años las plataformas web pasaron a delegar
en el navegador y en JavaScript muchas de las tareas que antes sucedían del lado del
servidor. Por lo cual hoy en día los sitios web están cada vez más basados en JavaScript, y
desarrollando funcionalidades más complejas en el front-end.
JavaScript comenzó siendo un lenguaje orientado a la web, y por muchos años el navegador
fue su único contexto posible. Sin embargo, hoy en día se puede utilizar JavaScript para
proyectos que van mas allá de los sitios web, como pueden ser aplicaciones móviles,
aplicaciones de escritorio, servicios web, APIs, infraestructuras de back-end, juegos, IoT, etc.
Por lo tanto, conocer JavaScript a fondo nos va a abrir una infinidad de posibilidades en los
relacionado no solo al desarrollo web, sino al mundo del desarrollo de software en general.
El Estándar ECMAScript
ECMAScript es una especificación de lenguaje de programación en la que se basa JavaScript.
La mayoría de navegadores web incluyen una implementación propia del estándar
ECMAScript para interpretar JavaScript. Por ejemplo, Google desarrolló su propio intérprete
de JavaScript para Chrome, llamado V8, que es también el motor de [Link]. Firefox, por su
parte, hizo lo propio creando SpiderMonkey. Y JavaScriptCore es la versión creada y utilizada
en Safari.
Las versiones vigentes de ECMAScript que existen al momento de escribir esto son:
5ta Edición
6ta Edición - ECMAScript 2015
7ma Edición - ECMAScript 2016
8va Edición - ECMAScript 2017
9na Edición - ECMAScript 2018
10ma Edición - ECMAScript 2019
Requerimientos Previos
Para comprender este libro no hace falta tener conocimientos previos en programación. Es
recomendable contar con alguna experiencia básica en HTML y CSS, y tener alguna práctica
mínima en la creación de páginas web con estos lenguajes. De esta forma, contarás con las
herramientas básicas para comenzar a crear código, y además podrás comprender mejor la
interacción de JavaScript con documentos web y lo que llamamos el DOM.
Herramientas Útiles
Para comenzar a crear código JavaScript podemos utilizar diferentes herramientas. La
principal sería un editor de código. Uno de los más populares en el ámbito del desarrollo
web, y que personalmente utilizo hoy en día es Visual Studio Code.
Para probar los ejemplos de este libro, así como cualquier fragmentos de código JavaScript
mientras aprendes el lenguaje, te recomiendo tener a mano el navegador Google Chrome, y
utilizar la herramienta de desarrollo o Developer Tools, también conocida como la consola, a
la que puedes acceder presionando CTRL + CMD + i en MacOS, o CTRL + SHIFT + i en
Windows.
En algunas ocasiones, ciertos fragmentos de código no devolverán un valor en la consola.
Para esos casos te recomiendo una herramienta llamada RunJS que nos permite evaluar
cualquier expresión de JavaScript en tiempo real para ver su resultado de forma aun más ágil.
Por último, muy buenas existen herramientas online para ejecutar nuestro código. Algunas
de ellas son:
CodePen
CodeSandbox
JSBin
StaticBlitz
Herramientas Adicionales
Para obtener recursos adicionales y facilitar tu aprendizaje, el libro cuenta con un
repositorio en Github con material que voy actualizando periódicamente. No es necesario
contar con una cuenta en Github, pero si no tienes una te recomiendo crearla ya que te será
muy útil.
TERMINOLOGÍA Y SINTAXIS
Literales
Un literal es un valor exacto, representado por una sintaxis específica. En otras palabras, la
forma de escribir literalmente un valor.
Number(1024)
String("Hola mundo")
Object({ clave: valor })o
Array(0, 2, 4)
Boolean(true)
Expresiones y Sentencias
Una expresión (en inglés expression) es una unidad de código que JavaScript evalúa, y
devuelve a partir de ella un valor único.
Una sentencia (en inglés statement) es una unidad más compleja de código. Puede estar
compuesta por una o más líneas, y contener múltiples expresiones.
// sentencias de declaración
var x = 0;
let y = true;
const z = "";
// sentencia condicional
if (condicion) {
// bloque de código
}
// sentencia de iteración
while (condicion) {
// bloque de código
}
Para finalizar una sentencia y separarla del resto se utiliza el punto y coma (;).
Personalmente tiendo a no utilizar punto y coma en mi código, excepto que trabaje sobre
código existente que ya lo usa. Esto es una decisión personal, ya que considero que el código
se ve más limpio de esta forma.
Sin embargo, mientras estés en etapa de aprendizaje, te recomiendo utilizar punto y coma
en todos los casos para evitar errores en tu código.
Comentarios
En JavaScript podemos utilizar una sintaxis especial para escribir comentarios entrelazados
con nuestro código, sin que afecten la ejecución normal del mismo.
Los comentarios son útiles para colocar notas sobre la intención de un determinado bloque
de código o para escribir primero nuestras ideas y luego transformarlas una a una en código,
entre otros usos posibles.
En los ejemplos de código de este libro aparecerán muchas veces comentarios que
representan el resultado de una expresión de JavaScript, o simplemente alguna nota sobre el
código mismo.
/*
Barra y asterísco para comentarios
que ocupan múltiples líneas.
*/
/***
* Puedes crear cualquier otra variante
* de los dos anteriores para generar
* comentarios con mejor estilo visual.
*/
TIPOS DE DATOS
En JavaScript existen dos categorías de datos: los primitivos y los objetos.
Primitivos
Los tipos primitivos son los tipos de datos más básicos de JavaScript. Al día de hoy, el
lenguaje cuenta oficialmente con 6 tipos primitivos:
Number
String
Boolean
Null
Undefined
Symbol
Number
El tipo Number abarca todos los datos de tipo numérico. Pueden ser números enteros o
decimales, como también otros tipos de valores numéricos como hexadecimales, binarios, y
octales.
256 // entero
3.14 // decimal
1e5 // notación de exponenciación (100000)
1e-5 // notación de exponenciación (0.00001)
0644 // octal (anteponiendo 0) (420)
0o10 // octal (anteponiendo 0o) (8)
0b1000 // binario (anteponiendo 0b) (8)
0xFF // hexadecimal (255)
String
Para crear lo que llamamos un literal de string, simplemente encerramos nuestra cadena de
caracteres entre comillas dobles (") o comillas simples (') (el resultado es el mismo, esta
elección es simplemente una cuestión de preferencia). Y a partir de ECMAScript 2015,
también podemos definirlas utilizando tildes invertidas (`).
"Esto es un string"
"Esto también es un string"
`Esto es un string a partir de ES2015`
Boolean
El tipo Boolean o booleano tiene dos únicos valores posibles: true o false.
true
false
Null
El tipo Null tiene un único valor posible, null. Este representa la ausencia intencional de
valor, es decir, para indicar que un valor es nulo o está vacío.
null
Undefined
undefined
Cuando en JavaScript declaramos una variable sin asignarle un valor inicial, se le asigna
automáticamente el valor undefined.
var x;
x; // → undefined
Symbol
Symbol es un tipo de dato que aparece en ES2015, y se utiliza principalmente para crear
referencias únicas llamadas símbolos para identificar propiedades de objetos.
Symbol()
Symbol("descripcion")
Por ser un tipo de dato poco frecuente no profundizaremos sobre su uso en este libro.
BigInt aparece en ECMAScript 2021, que aún no es oficial al momento de escribir este libro.
Por lo tanto es el más nuevo de los tipos primitivos.
Fue creado para trabajar con números enteros más grandes de los que soporta el tipo
Number.
84920323940298340n
// o bien
BigInt(84920323940298340)
Al igual que Symbol, dado que BigInt es un tipo de dato nuevo y aun casi no utilizado, no
ahondaremos en él.
Además, son inmutables, es decir, no pueden ser modificados. Pueden ser reemplazados por
otros valores luego de ser asignados a una variable, como veremos más adelante, pero no
pueden ser alterados en sí mismos. Son valores de sólo lectura.
Para comprender mejor los tipos primitivos, te voy a proponer que de ahora en más,
pensemos en ellos como valores que existen antes de que los nombremos, en una especie
de universo paralelo al que podemos llamar universo primitivo.
Entonces, tanto null, undefined, true, false, 3.14, y “tres tristes tigres”, así
como todos los números y cadenas de caracteres que podamos imaginar, habitan este
universo primitivo, y nosotros simplemente los referenciamos cuando los necesitamos.
“Universo Primitivo”
Objetos
La otra categoría de estructuras de datos en JavaScript son los objetos. Un objeto es un
constructo, una estructuras más complejas que un primitivo. Podríamos definirlo como una
colecciones de propiedades.
En JavaScript es posible crear nuestros propios objetos. Por ejemplo, un literal de objeto
consiste en una serie de propiedades y valores, separados por comas, y encerrados entre
llaves.
var automovil = {
marca: "Audi",
año: 2020,
valor: null,
caracteristicas: { abs: true }
}
JavaScript nos provee también de una serie de objetos globales o nativos, que nos son
útiles para diferentes tareas. Por ejemplo, el objeto Date nos permite trabajar con fechas y
datos temporales, o el objeto Math, que nos sirve para realizar diversas operaciones
matemáticas y numéricas complejas.
// método de redondeo
[Link](3.1415) // → 3
JavaScript cuenta además con dos subtipos de objetos que poseen características distintivas:
el array y la función.
Array
// array de strings
["lunes", "martes", "miercoles", "jueves", "viernes"]
// array numérico
[1, 1, 2, 3, 5, 8]
// array mixto
[26, true, "js", new Date(), {}]
Función
Por su parte, una función es un tipo de objeto que posee la particularidad de que, al ser
declarado, no se ejecuta inmediatamente, sino que se guarda en memoria para ser invocado
más tarde, una o más veces.
// o bien
// invocamos a la función
miPrimerFuncion() // →"Hola mundo"
OPERADORES
Ya conocemos los tipos de datos que podemos manipular en JavaScript. Lo siguiente que
vamos a necesitar saber es qué hacer cosas con esos datos.
Para esto vamos a necesitar conocer los diferentes operadores que nos van a permitir
precisamente realizar todo tipo de operaciones sobre los datos para conocerlos, compararlos,
modificarlos y crear nuevos.
Operadores aritméticos
Suma (+)
3 + 4 // →7
Resta (-)
5 - 2 // →3
Multiplicación (*)
6 * 4 // →24
División (/)
8 / 2 // →4
Módulo (%)
También llamado resto, Devuelve el resto de la división de dos operandos.
9 % 4 // →1
Incremento (++)
Incrementa en una unidad al operando. Si es usado antes del operando (++x) devuelve el
valor del operando después de añadirle 1, y si se usa después del operando (x++) devuelve el
valor de este antes de añadirle 1.
var x = 0;
var y = ++x; // incrementa x a 1, luego devuelve 1
var z = x++; // devuelve 1, luego incrementa x a 2
x; // → 2
y; // → 1
z; // → 1
Decremento (–)
Resta una unidad al operando. La posición con respecto al operando tiene el mismo
comportamiento que el operador de incremento.
var x = 2;
var y = --x; // decrementa x a 1, luego devuelve 1
var z = x--; // devuelve 1, luego decrementa x a 0
x; // → 0
y; // → 1
z; // → 1
-"3" // → -3
-true // → -1
Unario Positivo (+)
+"3" // → 3
+true // → 1
Exponenciación (**)
2 ** 3 // → 8
Operadores Relacionales
Los operadores relacionales comparan sus operandos y retorna un booleano como
resultado.
in
propiedad in objeto
// → edad -> 21
// → sexo -> "Masculino"
// → ojos -> "Marrones"
instanceof
Devuelve true si el objeto especificado como primer operando es del tipo especificado en el
segundo operando.
Operadores de Comparación
Los operadores de comparación también pertenecen a la categoría de operadores
relacionales.
Igualdad (==)
x == y
0 == false // → true
2 == "2" // → true
"" == 0 // → true
false == "" // → true
null == undefined // → true
Esto no es un error, sino que es debido al algoritmo que utiliza el operador internamente
para realizar la comparación. Este tema lo abordo con más detalle en el capítulo sobre
coerción.
x === y
Desigualdad (!=)
x != y
0 != false // → false
2 != "2" // → false
"" != 0 // → false
false != "" // → false
null != undefined // → false
x !== y
Devuelve true si los operandos no son del mismo tipo y/o valor.
x > y
x < y
x <= y
Operadores de asignación
Asignación (=)
x = y
x = "JS";
y = 43;
z = y;
x += y
Equivale a x = x + y.
Asignación de sustracción (-=)
x -= y
Equivale a x = x - y.
x *= y
Equivale a x = x * y.
x /= y
Equivale a x = x / y.
x %= y
Equivale a x = x % y.
x **= y
Operadores Lógicos
AND (&&)
El valor producido por && no es necesariamente de tipo boolean, sino que es siempre el valor
de uno de sus dos operandos.
x && y
OR (||)
El valor producido por || no es necesariamente de tipo boolean, sino que es siempre el valor
de uno de sus dos operandos.
x || y
var x = false;
x || [Link]("Dev"); // → "Dev"
NOT (!)
!x
Devuelve un booleano contrario al valor del operando. Si el operando es verdadero, devuelve
false. De lo contrario, devuelve true.
!true // → false
!false // → true
!"Perro" // → false
!0 // → true
!1 // → false
!undefined // → true
!null // → true
![] // → false
!{} // → false
Se suele utilizar también como !!, que no es más que dos negaciones consecutivas, logrando
el resultado exactamente opuesto, es decir, convertir el operando a su versión booleana.
!!true // → true
!!false // → false
!!"Perro" // → true
!!0 // → false
!!1 // → true
!!undefined // → false
!!null // → false
!![] // → true
!!{} // → true
obtenerEstado(14); // → "menor"
obtenerEstado(32); // → "adulto"
Operadores Unarios
typeof
typeof operando
typeof (operando)
Devuelve una string indicando el tipo del operando evaluado. Los paréntesis son opcionales.
void
void expresion
void (expresion)
Previene que una expresión devuelva un valor. Los paréntesis son opcionales.
Su uso es poco frecuente, y se utiliza muchas veces para ejecutar una expresión en JavaScript
desde un link HTML sin provocar efectos secundarios.
<a href="javascript:void(0)">Este link no hace nada</a>
delete
delete [Link]
delete array[indice]
var miObjeto = { x: 1, y: 2 };
var miArray = ["gato", "perro", "conejo"];
delete miObjeto.x;
// → true
// (la propiedad x se ha eliminado)
delete miObjeto;
// → false
// (no puede eliminar un objeto)
delete [Link];
// → false
// (no puede eliminar propiedades predefinidas de un objeto
global)
delete miArray[2];
// → true
// (el valor "conejo" fue eliminado)
miArray[2]; // → undefined
Operador Coma
El operador coma (,) evalúa dos operandos y retorna el valor del último.
Este operador se utiliza principalmente dentro de un bucle for, permitiendo declarar
múltiples variables y evaluarlas en cada iteración.
Además, también se puede utilizar para declarar múltiples variables en una misma
expresión.
var a, b, c;
var d = 0,
f = true,
f = "Usuario";
VARIABLES
Las variables son un componente importante de muchos de los lenguajes de programación,
incluido JavaScript. Una variable es esencialmente una referencia, que nos permite
etiquetar un valor y guardarlo en la memoria para utilizarlo más tarde.
Para declarar una variable, lo hacemos utilizando la palabra clave var, seguida de un
nombre que la identifique.
Las variables nos sirven para reutilizar valores, realizar operaciones sobre ellos, pasarlos
como argumentos a funciones, entre otros.
var valor1 = 3;
var valor2 = 2;
// operación de multiplicación
valor1 * valor2; // → 6
En lugar de eso, te propongo imaginar que declarar una variable es como crear una etiqueta,
y que al asignarle un valor, estamos tendiendo un hilo que conecta esta etiqueta con el valor.
var mascota = "Perro";
Asignación e Igualdad
Dicho esto, vale la pena también hacer una aclaración adicional:
Es frecuente que se confundan las variable con valores. Una variable no es un valor,
sino que hace referencia a un valor.
Siguiendo con el ejemplo anterior, declaramos ahora una nueva variable animal, y le
asignamos también el valor "Perro".
Cuando hablamos de los tipos primitivos, dijimos que ellos existen antes que los
nombremos, en un universo primitivo, y que nosotros simplemente los evocamos o
referenciamos. Entonces, siguiendo esta lógica, como el string "Perro" ya existe, las dos
variables mascota y animal van a estar conectadas al mismo valor.
Al igualar animal a mascota, lo que estamos haciendo es unir ambas etiquetas animal y
mascota al mismo valor primitivo.
En ambos ejemplos, las dos variables referencian al string "Perro", y por lo tanto al
compararlas con el operador de igualdad estricta nos arrojará true.
Y es que ambas variables son ahora equivalentes al literal de string "Perro". Y "Perro" es
siempre igual a "Perro".
Supongamos ahora que más adelante en nuestro código queremos reemplazar el valor de
mascota por el string "Gato".
// luego...
mascota = "Gato";
animal; // ???
Sin embargo, la variable animal sigue conectada al string "Perro", y por lo tanto ese
seguirá siendo su valor.
var mascota = "Perro";
var animal = mascota;
mascota = "Gato";
animal; // → "Perro"
En este ejemplo podemos ver claramente por qué, si pensáramos las variables como cajas,
podríamos creer que animal contiene a mascota, y por lo tanto al modificar en valor de
mascota, estaríamos cambiando también el valor de animal, lo cual sería incorrecto.
Las variables se conectan con valores, y no con otras variables. Siempre de un lado del
hilo está la etiqueta, y del otro, el valor.
Declaramos una variable lista1 y le asignamos como valor un objeto array vacío. Luego
repetimos lo mismo con lista2.
var lista1 = [];
var lista2 = [];
Cada vez que asignamos a una variable un literal de objeto con [] o {}, estamos creando en
ese instante un objeto nuevo.
Es por eso que al intentar comparar objetos o arrays con el operador de igualdad, el resultado
será siempre false.
Siguiendo con el ejemplo, insertamos ahora un string "x" en la posición 0 del array lista1.
var lista1 = [];
var lista2 = [];
lista1[0] = "x";
Con lo que ya sabemos, podemos tener la certeza de que, al ser arrays diferentes, al modificar
uno, no alteraremos el otro.
Fácil ¿verdad?
lista1[0] = "x";
lista2 = lista1;
lista2[0] = "y";
A partir del fragmento anterior ¿cuál sería la representación correcta? Tómate unos minutos
para refleccionarlo.
Vayamos paso a paso.
lista2 = lista1;
El array que antes estaba conectado a lista2 ahora quedó desconectado de toda variable, y
por lo tanto partir de ahora es inalcanzable, por lo que JavaScript lo va a eliminar para
liberar memoria, en un proceso llamado Garbage Collection.
Ahora, lista1 y lista2 están unidas (referencian) al mismo array, que a estas alturas
equivale a ["x"], y por lo tanto cualquier alteración o mutación en este array se reflejará en
ambas variables.
lista2[0] = "y";
lista1; // → ["y"]
lista2; // → ["y"]
En este caso sí, al comparar ambas referencias, obtendremos un resultado positivo.
Tipado en JavaScript
JavaScript es un lenguaje llamado de tipado dinámico. Esto quiere decir que, mientras en
otros lenguajes es necesario hacer explícito el tipo de variables al declararla (por ejemplo, si
es de tipo string, boolean, etc), en JavaScript las variables no tienen tipo, sino que el
tipo (en inglés type) es una característica del valor.
Es por eso que en JavaScript podemos asignarle a una variable un valor de cualquier tipo, por
ejemplo string, y más tarde reemplazarlo por otro, por ejemplo un number, sin provocar
ningún error.
Si bien el lenguaje nos brinda esta posibilidad, asignar a una variable diferentes tipos de
datos a lo largo de una misma aplicación puede generar inconsistencias y confusión si no
somos extremadamente cuidadosos y explícitos, por lo cual te recomiendo evitarlo siempre
que sea posible.
var a = "Ariel";
typeof a; // → "string"
a = 34;
typeof a; // → "number"
a = true;
typeof a; // → "boolean"
a = [];
typeof a; // → "object"
a = undefined;
typeof a; // → "undefined"
Conocer el Tipo de Valor
Para conocer el tipo de valor podemos utilizar el operador typeof, como ya vimos
ejemplificado anteriormente.
En el caso de un array, typeof nos devuelve "object" porque, en rigor, un array es un tipo
de objeto. Para evitar confundirlo con cualquier otro tipo de objeto, podemos utilizar el
método [Link]().
Por otra parte, null es en rigor un dato de tipo Null según la especificación de ECMAScript.
Sin embargo, typeof nos arroja que es un "object". Esto se debe a un bug que data de las
primeras versiones de JavaScript, y que se ha decidido deliberadamente no corregir, ya que
esto podría provocar otros bugs de forma masiva.
Por lo tanto, para saber si una variable es null, podemos utilizar el operador de igualdad
estricta ===:
var n = null;
var a = 1;
function scope () {
a = 3;
var b = 4;
a; // → 3
b; // → 4
}
a; // → ?
b; // → ?
scope();
a; // → ?
b; // → ?
En JavaScript el ámbito de las variables declaradas con var lo define la función. Por lo tanto,
las variables tienen un alcance limitado al contexto de la función dentro de la cual son
declaradas, es decir, quedan inaccesibles fuera de ese ámbito o scope.
En el ejemplo, la primera vez que llamamos a a fuera de la función, esta preserva el valor con
el que fue declarada en la primera línea. Por lo tanto su valor es 1. b sin embargo no fue
declarada, y por lo tanto devuelve undefined.
Al invocar la función scope(), se les asigna un nuevo valor a a, y se declara una variable b.
Ámbas están ahora disponibles dentro del ámbito de la función.
A partir de ES2015, let y const nos permiten declarar variables que cuyo ámbito funciona
de manera diferente. Esto lo desarrollo en detalle en el capítulo sobre ECMAScript 2015.
Nombrar Variables
Al elegir el nombre de una variable debemos tener en cuenta ciertas reglas y limitaciones.
El código JavaScript se ejecuta, a grandes rasgos, en un orden líneal, desde la primera línea
de código hasta la última. Sin embargo, este orden se altera cuando el intérprete (el
navegador) encuentra un bloque de código que cambia el flujo.
A los bloques de código que tienen la capacidad de alterar el flujo de ejecución de nuestro
código JavaScript se les conoce como estructuras de control.
Sentencia de bloque
La sentencia de bloque es una estructuras sencilla, que se utiliza simplemente para agrupar
declaraciones, y no modifica el flujo de ejecución.
{
var mascota = "Perro";
[Link]("Mi mascota es " + mascota);
}
Sentencia condicional
La sentencia condicional se utilizan para ejecutar un bloque de código únicamente si se
cumple cierta condición.
Por ejemplo:
if (edad >= 18) {
[Link]("Eres mayor de edad");
} else {
[Link]("Eres menor de edad");
}
if (
diaDeLaSemana === "sabado"
|| diaDeLaSemana === "domingo"
) {
descansar();
} else if (diaDeLaSemana === "viernes") {
prepararseParaElFinDeSemana();
} else {
trabajar();
}
Sentencias de iteración
Las sentencias de iteración, también llamadas bucles o loops, se utilizan para ejecutar un
bloque de código de forma reiterativa, y basada en alguna condición.
while
while (i > 0) {
[Link]("i es igual a " + i);
i--;
}
do … while
La sentencia do…while funciona igual que while, pero con la diferencia de que el bloque de
código se ejecuta al menos una vez, independientemente del valor de la condición.
var i = 5;
do {
[Link]("i es igual a " + i);
i--;
} while (i > 0);
do {
[Link]("Me veras solo una vez");
} while (true === false);
La sentencia for recibe tres argumentos que determinan la forma de iteración del bloque de
código.
Una expresión inicial que se ejecuta una sola vez, antes de la primer iteración;
Una condición que debe cumplirse para ejecutar el bloque de código;
Una expresión final que se ejecuta al final de cada iteración;
Por lo tanto, el bucle va a iterar sobre el bloque de código tantas veces como se cumpla la
condición, y teniendo disponible dentro de su contexto los valores de las expresiones inicial
y final.
// → "vuelta 1"
// → "vuelta 2"
// → "vuelta 3"
// → "vuelta 4"
// → "vuelta 5"
Aunque no es su único uso, es común el uso del bucle for para iterar sobre arrays, escrito de
la siguiente forma:
var automovil = {
marca: "Audi",
modelo: "A1",
año: 2020
}
// → "marca es Audi"
// → "modelo es A1"
// → "año es 2020"
Sentencia switch
La sentencia switch evalúa una expresión, comparando su valor con otro valor llamado
case. Cuando los valores coinciden, se ejecuta el código comprendido desde el case hasta el
final del bloque switch, o hasta encontrar una sentencia break.
Si ninguno de los case coincide con la expresión evaluada, se buscará una sentencia
default, y si esta presente, se ejecutará el código que le sigue.
var ahora = new Date();
switch (diaDeLaSemana) {
case 0:
[Link]("Domingo");
break;
case 1:
[Link]("Lunes");
break;
case 2:
[Link]("Martes");
break;
case 3:
[Link]("Miércoles");
break;
case 4:
[Link]("Jueves");
break;
case 5:
[Link]("Viernes");
break;
case 6:
[Link]("Sábado");
break;
default:
[Link]("Día inexistente");
}
Sentencia break
La sentencia break interrumpe abruptamente la ejecución de un bloque, sentencia de
iteración o sentencia switch.
// dado un bucle `for...in`
for (var x in objeto) {
Sentencia continue
La sentencia continue interrumpe la iteración en curso, y hace que se continúe con la
siguiente.
var automovil = {
marca: "Audi",
modelo: "A1",
año: 2020
}
// → "marca es Audi"
// → "año es 2020"
FUNCIONES
Una función es un bloque de código capaz de realizar una o más tareas, y luego devolver un
valor como resultado.
La sentencia return es la que indica qué valor devolverá la función. Es opcional, y cuando
no se especifica un valor de retorno, la función devuelve undefined.
El código dentro de una función es ejecutado una vez que la función es invocada, utilizando
su nombre de identificador seguido de ().
sumar(); // → 4
Este ejemplo nos muestra una función que suma 2 + 2, y nos retorna el resultado.
En la práctica, esto no sería muy util, ya que si quisiéramos por ejemplo realizar la operación
suma sobre otros valores, tendríamos que crear una nueva función para cada combinación
posible.
Parámetros y Argumentos
Para hacerla mas dinámicas, las funciones aceptan argumentos, que son valores que
podemos pasarle a la función al invocarla con los ().
Para determinar qué argumentos acepta una función, al momento de declararla le definimos
parámetros, entre los paréntesis y separados por ,:
Los argumentos quedan disponibles como variables dentro del ámbito de la función. Ahora,
nuestra función sumar() recibe dos argumentos que serán los operandos de la suma, y por
lo tanto es mucho más versátil que su versión anterior. Por ejemplo:
sumar(4, 3); // → 7
sumar(100.51, 3.1415); // → 103.6515
Una función puede definir 0 o más parámetros, y aceptar 0 o más argumentos. Además puede
no ser necesario pasarle a una función valores para todos sus parámetros, pero sí es
importante el orden en que se los pasemos, que debe corresponder con el orden establecido
al declarar la función.
También es posible pasarle argumentos a una función que no declarar ningún parámetro, ya
que los argumentos son accesibles dentro del ámbito de la función en el objeto arguments.
Por ejemplo, nuestra función sumar podría declararse sin parámetros de la siguiente
manera:
function sumar () {
return arguments[0] + arguments[1];
}
sumar(6, 3); // → 9
BOOLEANOS
Un boolean es un dato binario, es decir, tiene solo dos valores posibles, true y false.
El tipo booleano es util cuando en nuestro código necesitamos distinguir entre dos
posibilidades, por ejemplo:
verdadero / falso
prendido / apagado
si / no
0 == "0" // → true
0 === 1 // → false
0 != "1" // → true
0 !== 1 // → true
0 < 1 // → true
0 >= 1 // → false
!0 // → true
Los valores convertibles a false se conocen como falsy o falsey, y son aquellos que cuando
se encuentran en un contexto de booleano, JavaScript los convierte a false:
0 (cero)
-0 (cero negativo)
0n (cero de tipo BigInt)
"" (comillas dobles vacías)
'' (comillas simples vacías)
`` (tildes invertidas vacías)
null
undefined
NaN
Number()
La función global Number() nos permite crear valores de tipo number a partir de cualquier
valor.
Number(23) // → 23
Number(3.14) // → 3.14
Number("23") // → 23
Number("3.14") // → 3.14
Number(true) // → 1
Number(false) // → 0
Number(null) // → 0
Number(undefined) // → NaN
Number([]) // → 0
Number([0]) // → 0
Number([1]) // → 1
Number([2, 3, 4]) // → NaN
Number({}) // → NaN
Number({ a: 1, b: 2 }) // → NaN
Para conocer en mayor profundidad las posibilidades que nos da Number puedes visitar
este link.
Métodos de Number
parseInt()
parseFloat()
parseFloat("3.14"); // → 3.14
parseFloat("314e-2"); // → 3.14
parseFloat("0.0314E+2"); // → 3.14
[Link]("3.14 es PI"); // → 3.14
[Link]("PI es 3.14"); // → NaN
toFixed()
var PI = 3.1415;
[Link](2); // → "3.14"
[Link](3); // → "3.141"
[Link](0); // → "3"
[Link](); // → "3"
NaN
NaN es una propiedad global de JavaScript. Comúnmente se dice que NaN no es un número,
ya que si nombre proviene de *Not-a-Number“* (No-Numero). Sin embargo, si verificamos
su valor mediante el operador typeof, veremos que en JavaScript, (por especificación del
estándar ECMAScript) NaN es un valor numérico:
typeof NaN // → "number"
Por lo tanto, es más apropiado considerar a NaN como un número, y más precisamente, un
número inválido.
Muchos métodos de JavaScript retornan NaN cuando no pueden parsear sus parámetros
como números válidos.
Una de las formas en que podemos chequear si un valor es NaN es usando la función global
isNaN(), que devuelve un booleano según si el argumento que recibe es o no NaN.
isNaN(NaN) // → true
isNaN(1) // → false
Sin embargo, esta función también tiene ciertos corner cases, es decir casos en los que no
resulto como se esperaría:
isNaN() // → true
isNaN("Ariel") // → true
isNaN(undefined) // → true
Esto es debido a que los valores no numéricos que recibe isNaN() son coercionados al tipo
number antes de ser evaluados. Entonces, dado que cualquier string o undefined al ser
forzados a number devuelven NaN, isNaN(NaN) siempre devuelve true.
Number(undefined) // → NaN
Number("Ariel") // → NaN
// por lo tanto
isNaN(Number(undefined)) // → true
isNaN(Number("Ariel")) // → true
Tanto NaN como isNaN() también son accesibles como [Link] y [Link]().
[Link]([Link]); // → true
Infinity
Infinity es una propiedad global. En el navegador, las propiedades globales pertenecen al
objeto window. Por lo tanto también es posible referirnos a Infinity como
[Link].
6 * Infinity // → Infinity
8 / Infinity // → 0
Math
El objeto global Math cuenta con múltiples propiedades y métodos útiles para realizar
operaciones matemáticas. A continuación veremos algunos de los más destacados.
Para una referencia completa del objeto Math puedes visitar este link.
Math.E
Math.E // → 2.718281828459045
[Link]
[Link] // → 3.141592653589793
[Link]()
[Link](3.14) // → 3
[Link](5.79) // → 6
[Link](7.5) // → 8
[Link]()
[Link](82) // → 82
[Link](2.02) // → 3
[Link](4.001) // → 5
[Link](-4.001) // → -4
[Link]()
[Link]()
[Link](8, 3) // → 512
[Link]()
[Link](64) // → 8
[Link]()
[Link]() // → 0.784405114211054
[Link]() // → 0.81548726640262
[Link]() // → 0.81548726640262
[Link]() // → 0.07392369524727838
Si quisiéramos retornar un número entero dentro de un rango, debemos crear una función
como la siguiente, empleando [Link]():
function enteroAlAzar (min, max) {
return [Link]([Link]() * (max - min) + min);
}
0 1 2 3 4 5 6 7 8 9 10 11
E l g a t o g r i s
Por lo tanto, igual que en un array, podemos acceder a un caracter específico de un string
utilizando la siguiente sintaxis:
texto[0]; // → "E"
texto[2]; // → " "
texto[11]; // → "s"
Sin embargo, como ya hemos mencionado, los valores primitivos no son mutables. De
manera que, a diferencia de los array, los caracteres de un string no se pueden modificar.
texto[3] = "p";
texto[4] = "e";
texto[5] = "r";
texto[6] = "r";
texto[7] = "o";
String(3.14) // → "3.14"
String(true) // → "true"
String(false) // → "false"
String(function () {}) // → function () {}
String(undefined) // → "undefined"
String(null) // → "null"
String([1, 2, 3]) // → "1,2,3"
String({ a: 1, b: 2 }) // → "[object Object]"
String(NaN) // → "NaN"
String(Symbol()) // → "Symbol()"
String(2 * 3) // → "6"
String(true ? 0 : 1) // → "0"
Para conocer en mayor profundidad las posibilidades que nos da String puedes visitar
este link.
[Link]
length
[Link]; // → 23
indexOf()
El método indexOf() recibe como argumento un string, y devuelve un número que equivale
a la posición en la cual coincide por primera vez el argumento con el string inicial. Si no hay
coincidencias, devuelve -1.
[Link]("en"); // → 8
[Link]("gato"); // → 3
[Link](" "); // → 2
[Link]("perro"); // → -1
replace()
Si pasamos como valor buscado un string, replace reemplaza solo la primer coincidencia
en el string inicial.
Las expresiones regulares son unos patrones utilizados para encontrar combinaciones de
caracteres dentro de un string.
Para utilizarlas con el método replace y reemplazar todas las ocurrencias de un valor en
una cadena de texto, podemos utilizar el siguiente patrón como primer argumento:
/valor buscado/g
Nota como el valor buscado no está rodeado de comillas sino de barras /. La g al final
corresponde a global, y es la que indica que se debe buscar todas las ocurrencias del valor
dentro de una determinada cadena de texto.
concat()
El método concat() une (concatena) uno o más strings, y devuelve un string nuevo.
var nombre = "Lionel";
var nombreCompleto = [Link](" Andrés", " Messi");
trim()
El método trim() elimina cualquier espacio vacío antes y después del string, y devuelve un
nuevo string con el resultado.
split()
listaDeDias;
// → ["Lunes", "Martes", "Miercoles"]
toUpperCase() y toLowerCase()
Marca
Modelo
Color
Velocidad máxima
Capacidad de pasajeros
etc
Un automóvil tiene también la capacidad de realizar ciertas acciones, como darle arranque,
encender las luces, acelerar, frenar, subir o bajar los cambios, etc.
Los objetos de JavaScript funcionan de forma similar. Poseen características a las cuales
llamamos propiedades, algunas de las cuales podemos modificar, mientras que otras son de
solo lectura. Y también puede realizar acciones, mediante funciones, que al pertenecer a
objetos reciben el nombre de métodos.
JavaScript nos provee una serie de objetos globales que nos permiten realizar diferentes
funciones. Y además nos da la posibilidad de crear nuestros propios objetos.
Literales de Objeto
Un literal de objeto consiste en una serie de propiedades y valores, separados por comas, y
encerrados entre llaves.
var automovil = {
marca: "Ford",
precio: null,
caracteristicas: {
abs: true,
"velocidad máxima": 289.6,
}
};
Como vemos, nuestro objeto consiste en una serie de propiedades que pueden ser valores de
cualquier tipo, en este caso strings, null, números, booleanos e incluso otro objeto.
Para acceder a las diferentes propiedades de los objetos, lo hacemos con la siguiente
sintaxis:
[Link]; // → "Ford"
[Link]; // → null
[Link]; // → true
// o bien
automovil["marca"]; // → "Ford"
automovil["precio"]; // → null
automovil["caracteristicas"]["abs"]; // → true
Ambas sintaxis en este ejemplo se pueden usar indistintamente. Sin embargo, cuando el
nombre de una propiedad posee caracteres especiales o espacios (como en nuestro ejemplo
la propiedad "velocidad máxima"), debe ser declarada entre comillas, y solo se puede
acceder utilizando la sintaxis con corchetes.
Una vez creado un objeto, podemos adjuntarle nuevas propiedades más tarde.
[Link] = "Mustang";
[Link] = "Negro";
// o bien
automovil["modelo"] = "Mustang";
automovil["caracteristicas"]["color"] = "Negro";
[Link] = function () {
// implementación del método
}
Luego para invocar los métodos del objeto utilizamos la siguiente sintaxis:
[Link]();
[Link]();
Veamos una versión alternativa de nuestro objeto automóvil, esta vez con propiedades y
métodos implementados, que luego explicaremos bloque por bloque.
var automovil = {
estado: {
encendido: false,
luces: false,
velocidadActual: 0,
velocidadMaxima: 200
},
encender: function () {
[Link] = true;
[Link] = true;
return [Link];
},
apagar: function () {
[Link] = false;
[Link] = false;
return [Link];
},
acelerar: function () {
if ([Link] &&
[Link] <=
[Link]) {
[Link]++;
}
return [Link];
},
frenar: function () {
if ([Link] &&
[Link] > 0) {
[Link]--;
}
return [Link];
}
}
Nuestro automóvil ahora no solo tiene propiedades sino también métodos capaces de
realizar “acciones”.
Primero le asignamos a la propiedad estado un objeto que a su vez tiene otras propiedades
(que representan el estado del vehículo). Inmediatamente después, creamos 4 métodos que
se encargan de modificar el estado del propio objeto automóvil.
La palabra clave this, cuando se encuentra dentro de un método, hace referencia al objeto
que llama a dicho método. En este caso, como veremos, es el propio objeto automovil.
El método encender() modifica el estado de [Link] y
[Link], y devuelve el objeto estado.
[Link]();
// → {
// → encendido: true,
// → luces: true,
// → velocidadActual: 0,
// → velocidadMaxima: 200
// → }
[Link]();
// → {
// → encendido: false,
// → luces: false,
// → velocidadActual: 0,
// → velocidadMaxima: 200
// → }
Ahora si. ¿Qué es lo mejor que podemos hacer con un automóvil nuevo? ¡Vamos a darle una
vuelta!
[Link]();
[Link](); // → 1
[Link](); // → 2
[Link](); // → 3
[Link](); // → 4
Basándote en lo que ya vimos, ¿puedes inferir qué hace el método acelerar()? Tómate un
momento para analizarlo.
[Link]();
// → {
// → encendido: false,
// → luces: false,
// → velocidadActual: 0,
// → velocidadMaxima: 200
// → }
Objetos anidados
Siguiendo con los automóviles, tomemos como ejemplo los siguientes objetos:
var automovil1 = {
marca: "Audi",
caracteristicas: {
color: "Rojo"
}
}
var automovil2 = {
marca: "BMW",
caracteristicas: [Link]
}
[Link] = "Gris";
[Link] = "Gris";
[Link]; // → "Gris"
[Link]; // → "Gris"
En los casos en que lo deseado sea crear un nuevo objeto “clonado” a partir de otro, podemos
hacerlo utilizando el método [Link](), descrito en la sección siguiente. Por
ejemplo:
var automovil2 = {
marca: "BMW",
caracteristicas: [Link](
{},
[Link]
)
}
Métodos de Objeto
[Link]()
[Link](destino, ...objetos)
Por ejemplo:
var objeto = { a: 1, b: 2 };
[Link](objeto, { c: 2 });
objeto; // → { a: 1, b: 2, c: 3 }
Podemos utilizarlo también para crear nuevos objetos, sin mutar el objeto inicial,
simplemente asignándole como primer argumento un literal de objeto vacío:
var objetoInicial = { a: 1, b: 2 };
objetoInicial; // → { a: 1, b: 2 }
nuevoObjeto; // → { a: 1, b: 2, c: 3 }
[Link]()
var objeto = { a: 1, b: 2, c: 1 };
[Link]()
var objeto = { a: 1, b: 2, c: 1 };
[Link](objeto); // → [1, 2, 3]
Recuerda que estos se pueden utilizar también como funciones para crear nuevos datos:
String("JavaScript")
Number(2020)
Boolean(true)
Array([])
Object({})
Function('return')
Además, el lenguaje nos provee otros objetos con diferentes funcionalidades. Estos son solo
algunos de ellos:
Date
Math
Error
RegExp
Map
Set
JSON
Promise
Para una referencia completa de los objetos globales puedes consultar este link.
Como ya vimos, en JavaScript no solo hay objetos, sino también valores primitivos. Sin
embargo, al observar la sintaxis de ciertas expresiones de JavaScript, el ojo inexperto podría
cometer el error de caer en esa generalización.
Veamos un ejemplo:
(23).toFixed(2); // "23.00"
[Link](); // "true"
"Hola mundo".split('').reverse().join('');
// "odnum aloH"
Sobre algunos valores primitivos es posible llamar métodos. Pero lo que sucede en estos
casos no es ningún tipo de magia ni mecanismo misterioso.
Al invocar un método sobre un literal de primitivo como 23, true u "Hola mundo",
JavaScript comprende nuestra intención y convierte este valor en un objeto, de forma
implícita, y luego lleva a cabo nuestra instrucción.
Lo que sucede aquí no es otra cosa que el mecanismo de coerción actuando de forma
implícita, una vez más acudiendo en nuestra ayuda. Esta forma específica de coerción recibe
el nombre de boxing.
ARRAYS
Un array es una colección de elementos. Estos elementos pueden ser combinaciones de
cualquier tipo: primitivos u objetos.
Un array puede también tener posiciones vacías, o incluso estar completamente vacío.
Para crear un literal de array, simplemente envolvemos una serie de elementos entre
corchetes, y los separamos con comas.
// o bien
Podemos acceder a los elementos de un array así como insertar elementos nuevos utilizando
la siguiente sintaxis:
Supongamos que desde JavaScript hacemos una solicitud asíncrona a un servidor, para que
nos devuelva una lista de productos que se encuentran en un carrito de compras. Esa lista,
sea cual fuere el formato en el que la recibimos del servidor (JSON, XML, etc), es muy
probable que al recibirla debamos transformarla en un array, para luego poder procesarlo, e
iterar sobre sus elementos para mostrarlos en pantalla.
A continuación te presento algunos de los principales métodos de array, que nos van a ser
muy útiles en situaciones como esta.
Métodos de Array
Podemos conocer todos los métodos de Array disponibles explorando el prototipo de la
función global Array en la consola de Google Chrome mediante la siguiente expresión:
[Link]
[Link]
push()
El método push() inserta uno o más elementos al final de un array, y devuelve un número
con su nueva longitud length.
Cada valor que reciba el método como argumento es insertado al final del array. Como
resultado, el array original es modificado.
var animales = ["perro", "gato"];
[Link]("caballo"); // → 3
[Link]("vaca", "conejo"); // → 5
animales;
// → ["perro", "gato", "caballo", "vaca", "conejo"]
concat()
El método concat() concatena dos o más arrays, y devuelve como resultado un array
nuevo, sin modificar el array inicial.
Recibe como argumento cero o más arrays a concatenar con el array inicial. Si no se le pasa
argumentos, devuelve un nuevo array igual al anterior.
array4; // → [1, 2, 3, 4, 5, 6, 7, 8, 9]
map()
El método map() recibe como argumento una función, comúnmente conocida como
callback, y retorna un nuevo array con el resultado de aplicar esa función a cada uno de sus
elementos.
Por ejemplo:
var numeros = [2, 4, 6, 8, 0];
La función callback puede recibir cualquier nombre, o ser anónima. En este ejemplo la
llamamos sencillamente callback.
También se puede usar como argumento de map(), al igual que de cualquier otra función,
una referencia a una función declarada por separado, lo cual contribuye a que el código sea
más fácil de leer.
reduce()
El método reduce() recibe como argumento una función reductora, y retorna un único
valor basado en la aplicación esa función a cada uno de sus elementos.
Además, el método acepta un segundo argumento opcional para indicar un valor inicial.
var numeros = [0, 1, 1, 2, 3, 5, 8];
suma; // → 20
El mismo código del ejemplo anterior podría también escribirse de la siguiente forma:
suma; // → 20
filter()
El método filter() recibe como argumento una función filtro, y retorna un nuevo array
con los elementos que cumplan la condición implementada por esa función.
El nombre de este método puede generar confusión, dado que estamos acostumbrados a que
cuando hablamos de un filtro, solemos pensarlo como algo que excluye, es decir, lo que
podríamos llamar un filtro exclusivo.
Sin embargo, la función filtro en JavaScript se encarga de detectar a los valores que serán
incluídos en el resultado, y es por lo tanto un filtro inclusivo.
Es un proceso que suele ser desconocido, o en ocasiones simplemente evitado por algunos
programadores, argumentando que es un comportamiento errático o un defecto de
JavaScript.
Coerción Implícita
La coerción implícita es la que lleva a cabo JavaScript de forma automática.
En el capítulo sobre booleanos hablamos de valores truthy y falsy. Lo que hace posible que
valores de diferentes tipos sean convertibles a booleano en determinados contextos, no es
otra cosa que un proceso de de coerción implícita. Por ejemplo, dado el siguiente código:
var numero = 1;
var texto = "";
if (numero) {
[Link]("numero es verdadero");
}
texto
? [Link]("texto es verdadero")
: [Link]("texto es falso");
// → "texto es falso"
if (Boolean(numero)) {
[Link]("numero es verdadero");
}
Boolean(texto)
? [Link]("texto es verdadero")
: [Link]("texto es falso");
// → "texto es falso"
1. Si ambos valores son del mismo tipo, el operador == utiliza el mismo mecanismo que
===.
3 == "3" // true
// dado que...
Number("3") // → 3
3 == Number("3") // → true
1 == true // → true
// dado que...
Number(true) // → 1
1 == Number(true) // → true
0 == false // → true
// dado que...
Number(false) // → 0
0 == Number(false) // → true
Por último, los objetos son convertidos a valores primitivos. Ahora bien, esta conversión a
primitivo también sucede según una serie de reglas sobre las cuales no vamos a profundizar
aquí. Lo recomendable es ser consciente en todo momento de los tipos de datos que estamos
comparando, para evitar obtener resultados inesperados.
Boxing
Un tipo particular de coerción implícita es el llamado boxing, que sucede cuando invocamos
una propiedad o método sobre un valor primitivo. En estos casos, JavaScript promueve el
valor al tipo object, para asi poder llevar a cabo nuestro requerimiento.
Por ejemplo:
"JavaScript".length String("JavaScript").length
(23.5).toFixed(3) Number(23.5).toFixed(3)
[Link](3) Number(23.5).toFixed(3)
[Link]() Boolean(true).toString()
Coerción Explícita
La coerción explícita es aquella que logramos intencionalmente mediante el uso de
funciones u operadores. Por ejemplo:
// coerción a boolean
var numero = 42;
Boolean(numero); // → true
!numero; // → false
!!numero; // → true
// coerción a string
String(numero); // → "42"
numero + ""; // → "42"
`${numero}`; // → "42"
[Link](); // → "42"
// coerción a number
var texto = "42";
Number(texto); // → 42
+texto; // → 42
++texto; // → 43
texto++; // → 43
TEMPORIZADORES
En JavaScript contamos con dos métodos globales que nos permiten llamar a una función
luego de un tiempo determinado.
setTimeout()
El método global setTimeout() recibe como primer argumento la función a llamar o una
referencia a ella.
Si deseamos cancelar la llamada a la función antes de los 2000 ms, podemos hacerlo
utilizando el método global clearTimeout() pasándole como argumento la referencia al
temporizador.
clearTimeout(temporizador);
setInterval()
El método global setInterval() funciona de la misma forma que setInterval(), con la
única diferencia que la función se sigue llamando indefinidamente en los intervalos
indicados, hasta que la detengamos o cerremos la ventana del navegador.
setInterval(funcion, delay, ...argumentos)
Por ejemplo:
// ...
clearInterval(intervalo);
MANIPULANDO EL DOM
El DOM o Document Object Model, lo que podríamos traducir como Modelo de Objetos del
Documento, es una representación, estructurada y orientada a objetos, de un documento
web.
A través de JavaScript podemos interactuar con esta estructura y modificarla, y así alterar la
representación en el navegador de nuestros documentos web, su estilo, su estructura, y su
contenido.
La estructura del DOM tiene una jerarquía de árbol. Por eso es normal referirse a él como
Árbol del DOM (DOM Tree).
En las ramas de ese árbol se encuentran nodos, cada uno de los cuales representa un
elemento del documento.
<html>
<head>
<title>Ejemplo</title>
</head>
<body class="home">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</body>
</html>
Una vez parseado este código por el navegador, el HTML pasa a convertirse en un árbol de
nodos que podría representarse gráficamente este de la siguiente forma:
Árbol del DOM
JavaScript nos permite interactuar con este árbol de nodos de la misma forma que
interactuamos con otros objetos. Podemos leerlos, modificarlos, eliminarlos y agregar
nuevos.
El DOM, entonces, es la API que le permite a JavaScript realizar una de sus tareas más
importantes en el contexto de un documento web, como es la de controlar y manipular sus
elementos.
En esta sección vamos a explorar las formas de interactuar con el DOM utilizando JavaScript,
los objetos que tenemos disponibles para hacerlo, y sus principales métodos.
Selectores
En JavaScript contamos con una serie de métodos denominados selectores que nos permiten
seleccionar elementos de nuestro documento, y luego tratarlos como objetos: consultar y
modificar sus propiedades, e invocar sus métodos.
Veamos cómo funcionan estos métodos. Para eso nos basaremos en el siguiente fragmento
de HTML:
<ul>
<li id="primero" class="item">Item 1</li>
<li class="item">Item 2</li>
<li class="item">Item 3</li>
</ul>
getElementById()
Recibe como argumento un string, y retorna un único nodo cuyo atributo id se corresponda
con eél. Cabe recordar que en HTML, un valor de atributo id debe ser utilizado solo una vez
en cada documento.
primerItem;
// → <li id="primero" class="item">Item 1</li>
getElementsByClassName()
Recibe como argumento un string, y retorna una colección de nodos cuyo atributo class se
corresponda con él.
[Link]; // → 3
Cuando un selector, cualquiera sea, devuelve una colección de nodos, podemos contar la
cantidad de items que contiene mediante la propiedad length, así como acceder a los
diferentes elementos de esta colección, de la misma forma que lo hacemos en un array.
items[1];
// → <li class="item">Item 2</li>
getElementsByTagName()
Recibe como argumento un string que corresponde al nombre de una etiqueta HTML, y
retorna una colección de nodos que coinciden con esa etiqueta.
[Link]; // → 3
querySelector()
Recibe como argumento un string que corresponde con uno o más selectores CSS separados
por comas, y retorna el primer nodo que coincida con el selector.
primerItem;
// → <li id="primero" class="item">Item 1</li>
querySelectorAll()
Recibe como argumento un string que corresponde con uno o más selectores CSS separados
por comas, y retorna una lista de nodos que coincidan con el selector.
[Link]; // → 3
Insertando Contenido
Mediante las propiedades innerText e innerHTML podemos manipular el contenido de
texto y el HTML dentro de un elemento del DOM.
var items = [Link]("ul [Link]");
createElement()
El método createElement() nos permite crear un nuevo elemento nodo. Recibe como
argumento obligatorio el nombre del elemento a crear.
[Link](nombreDelElemento)
div; // → <div></div>
Como vemos, createElement() nos retorna un nuevo elemento <div>, que almacenamos
en la variable div.
¡Tranquilo! El <div> aun no se muestra en pantalla, solo esta guardado en memoria. Mas
adelante lo insertaremos en el DOM. Pero primero, vamos a hacerle algunas modificaciones.
Si ya haz creado documentos HTML en el pasado, sabes que un <div> vacío no nos mostrará
nada en patalla. Así que vamos a darle algo de contenido.
[Link] = "Aprendiendo JavaScript";
div;
// → <div>Aprendiendo JavaScript</div>
Aunque a decir verdad, un <div> no tiene ningún formato. Entonces, hagamos que su
contenido sea mejor un encabezado. Para eso utilizaremos la etiqueta <h1>.
encabezado;
// → <h1>Manipulando el DOM</h1>
Ya tenemos nuestros <div> y <h1> listos. ¿Cómo reemplazamos el viejo texto por nuestro
nuevo encabezado?
replaceChild()
El método replaceChild() nos posibilita reemplazar un nodo por otro. Recibe dos
argumentos, el nuevo nodo y el nodo a reemplazar.
[Link](nuevoNodo, viejoNodo)
Volviendo a nuestro <div>, vamos a cambiar su viejo texto plano por nuestro nuevo <h1>.
Para comenzar debemos saber que un texto es también en sí un nodo del DOM, en este caso
de tipo Text, mientras que nuestros <div> y <h1> son nodos de tipo Element.
Entonces, sabiendo esto, lo que queremos hacer ahora es reemplazar nuestro anterior nodo
de texto, por el nuevo nodo elemento.
Para ello nos serviremos con la propiedad childNodes que poseen los nodos del DOM, y
que nos retorna un objeto NodeList con los nodos hijo del nodo en cuestión.
var viejoNodoDeTexto = [Link][0];
[Link](encabezado, viejoNodoDeTexto);
div;
// → <div>
// → <h1>Manipulando el DOM</h1>
// → </div>
Ahora vamos a agregar un párrado a nuestro <div>. Para ello creamos un nuevo elemento
<p>.
Tenemos ahora un nuevo desafío. ¿Cómo insertamos el nuevo párrafo dentro de nuestro
<div> e inmediatamente después del <h1>?
appendChild()
[Link](parrafo);
div;
// → <div>
// → <h1>Manipulando el DOM</h1>
// → <p>Adjuntando nodos.</p>
// → </div>
¡Voilà! Nuestro div sigue creciendo, igual que nuestra habilidad para manipular el DOM.
¿Qué sucede si de repente recordamos (porque nos distrajimos entre tantos métodos) que
queríamos otro párrafo antes del actual?
Por suerte, una fracción de segundo después recordamos que queremos ser programadores
metódicos y responsables, y decidimos seguir explorando las posibilidades que nos da
JavaScript.
cloneNode()
Con el argumento true lo que hacemos es decirle a cloneNode() que queremos clonar el
nodo y todos sus nodos hijo, incluyendo los de texto.
Sin embargo, en nuestro ejemplo queremos un texto diferente, asi que clonaremos
únicamente el nodo, sin hijos, y luego le adjuntaremos nuestro texto deseado. Esta vez,
usaremos un nuevo método, createTextNode(), que funciona igual que
createElement() pero para nodos de texto.
parrafo2 = [Link]();
parrafo2; // → <p></p>
¡Ahora sí! Estamos un paso más cerca de nuestro objetivo de mostrar el contenido en
pantalla.
insertBefore()
El método insertBefore() acepta dos argumentos, un nuevo nodo a insertar y uno
existente de referencia, y como resultado, insertará el primero justo antes del segundo.
[Link](nuevoNodo, nodoExistente)
Usemos entonces este método para insertar nuestro nuevo <p> clonado antes de nuestro
anterior <p>.
[Link](parrafo2, parrafo);
div;
// → <div>
// → <h1>Manipulando el DOM</h1>
// → <p>JavaScript es genial!</p>
// → <p>Adjuntando nodos.</p>
// → </div>
Para lograrlo, debemos ahora adjuntarlo a cualquier elemento existente y visible. En este
caso, lo adjuntaremos directamente en el <body> de nuestro documento.
[Link](div);
Si el body de nuestro documento web estaba vacío, debería ahora verse de la siguiente forma:
<body>
<div>
<h1>Manipulando el DOM</h1>
<p>JavaScript es genial!</p>
<p>Adjuntando nodos.</p>
</div>
</body>
¡Felicitaciones! Hemos creado y manipulado elementos del DOM, y sin necesidad de ninguna
librería o framework.
En ocasiones tendremos la necesidad también de manipular elementos ya visibles del
documento. Para ello nos serviremos de los mismos métodos, en combinación con los
selectores.
Por ejemplo, ¿qué haríamos si quisiéramos remover uno de los párrafos del nuevo <div>?
remove() y removeChild()
[Link]();
[Link](nodoHijo);
Para remover uno de los párrafos dentro de nuestro <div> podemos hacerlo de dos formas:
si aun tenemos acceso a las variables que creamos anteriormente, podemos reutilizarlas para
referirnos a los mismos elementos del DOM que ahora son visibles. Si no, podemos volver a
crear referencias a esos elementos utilizando selectores.
// y luego modificamos
[Link]();
// o bien
[Link](parrafo);
Manipulando Atributos
Cuando necesitemos agregar, modificar y/o remover atributos de los diferentes nodos del
DOM, contamos con métodos para este fin.
setAttribute()
El método setAttribute() modifica un atributo del nodo, y si el atributo no existe, lo
crea.
[Link]("id", "contenedor");
[Link]("style", "color: blue;");
div;
// → <div id="contenedor">
// → <h1 style="color: blue;">Manipulado el DOM</h1>
// → ...
// → </div>
getAttribute()
[Link]("id"); // → "contenedor"
[Link]("style"); // → "color: blue;"
removeAttribute()
[Link]("id");
[Link]("style");
div;
// → <div>
// → <h1>Manipulado el DOM</h1>
// → ...
// → </div>
Manipulando Clases
El atributo class se utiliza frecuentemente para asociar estilos CSS a los elementos de
nuestro HTML. Poder modificar este atributo de forma dinámica con JavaScript nos resultará
de utilidad.
className
[Link] = "seccion";
[Link]; // → "seccion"
div;
// → <div class="seccion contenido">...</div>
classList
La propiedad classList nos devuelve un objeto que contiene las diferentes clases del
elemento, accesibles mediante su índice.
[Link];
// → ["seccion", "contenido", ...]
[Link][0]; // → "seccion"
[Link][1]; // → "contenido"
[Link]("contenido");
[Link]; // → "seccion"
div;
// → <div class="seccion principal">...</div>
[Link]("seccion");
[Link]; // → "principal"
[Link]("seccion");
[Link]; // → "seccion principal"
EVENTOS DEL DOM
Imaginemos por un momento que queremos saber en JavaScript cada vez que un botón de
nuestra página web es clickeado. ¿Cómo haríamos para resolver esta necesidad con lo que
conocemos hasta ahora?
Quizás nos veríamos tentados a crear una función que determine el estado del botón. Ahora
bien, para enterarnos lo suficientemente rápido si ese botón fue presionado o no,
deberíamos chequear el estado del botón una y otra vez.
Supongamos entonces que usamos la función setInterval(), que nos permita llamar a
esa función cada, digamos, 100 milisegundos. Esta implementación se vería algo así:
setInterval(function observarBoton () {
// determinar el estado del botón
}, 100);
A medida que nuestras páginas se volvieran más complejas, comenzarían también a volverse
ineficientes, al sobrecargarlas de llamados constantes a funciones por si acaso algo hubiera
sucedido. Esto causaría que el navegador consuma una cantidad innecesaria de memoria,
podría empezar a bloquearse la interfaz de usuario, etc.
La API de Eventos del DOM nos sirve para volver a nuestro JavaScript reactivo ante
determinados sucesos en nuestra interfaz web.
Algunos de los eventos más comunes que podemos controlar con JavaScript son:
Existen innumerables usos prácticos para los Eventos del DOM. Algunos de los que vemos
con frecuencia en los sitios con los que interactuamos a diario son:
Observando Eventos
Los elementos del DOM cuentan con el método addEventListener(), que nos permite
asociar un evento determinado con una función controlador de evento (event handler en
inglés).
[Link](
evento, // string
controlador, // función
usar_captura // booleano (opcional)
)
<button id="boton">Hola</button>
Observamos el evento click sobre el elemento boton. Cuando se dispara este evento
(cuando el usuario hace click en el botón), se invoca a la función controlador onClick, que
modifica el color de fondo del botón a rojo.
Analicemos ahora uno por uno los parámetros que recibe el método addEventListener().
Controlador de Evento
La función controlador es la que determina la acción a realizar al dispararse el evento.
Recibe como primer argumento un objeto de tipo Event, que contiene propiedades y
métodos relacionados con el evento disparado.
En el ejemplo anterior, el valor recibido por el parámetro event es más específicamente de
tipo MouseEvent, un tipo de evento “hijo” de Event, que recibe información adicional en
relación al mouse, como por ejemplo las coordenadas de la pantalla en las que se hizo click, y
qué botón del mouse se presionó.
Dentro de una función controlador, la palabra clave this hace referencia al elemento del
DOM que disparó el evento, en este caso el botón. En el ejemplo, this nos es equivalente a
utilizar la variable boton, ya que ambas hacen referencia a nuestro elemento <button>.
Evento
El parámetro evento consiste en un string correspondiente al nombre del evento a observar.
Existe una gran cantidad de eventos que podemos controlar en JavaScript. A continuación
nombro un pequeño grupo de los que se utilizan con mayor frecuencia:
click
dblclick
mouseover
mouseout
mousemove
keyup
keydown
Eventos de formularios
change
submit
focus
blur
load
error
resize
scroll
beforeunload
unload
Para una referencia completa de eventos disponibles y ejemplos de uso, visita este link.
Un ejemplo práctico
El evento click es uno de los que más frecuentemente nos tocará controlar. Se puede
observar en cualquier elemento de un documento HTML, excepto en algunos pocos como
<iframe>, <br> y otros a los que simplemente no es posible hacer click, como <head> o
<title>.
[Link]("click", mostrarContenido);
Creamos un elemento HTML <a> que será sobre el cual observamos los clicks, y un <div>
que inicialmente ocultamos con la clase hidden.
Luego con JavaScript observamos los click sobre el <a> con addEventListener(), y
establecemos una función mostrarContenido() como controlador.
Por último, utilizamos [Link] para modificar el texto del <a> según la visibilidad
del contenido. Para saber si el contenido esta visible o no, usamos
[Link]() para detectar la presencia de la clase hidden. Y luego utilizamos
un condicional ternario para decidir entre "Mostrar" y "Ocultar".
<html>
<body>
<div id="contenido">
<button id="boton">Clickeame</button>
</div>
</body>
</html>
Entonces queremos observar los clicks tanto en <body> como en <div> y <button>:
Los eventos se propagan en dos fases. La primera fase es la de captura (capture). Durante
esta fase el evento se propaga desde el objeto window hacia el nodo que disparó el evento,
llamado objetivo (target). En esta fase, el nodo objetivo es el último en manejar el evento.
Por lo tanto, el orden en que se manejarán los eventos dependerá lo que le comuniquemos al
respecto a addEventListener() al invocarlo.
[Link](
evento, // string
controlador, // función
usar_captura // booleano (opcional)
)
El tercer parámetro usar_captura es opcional y es el que vamos a usar para indicarle
nuestra intención de propagación.
Si deseamos manejar el evento en fase de captura, debemos asignarle el valor true. Si por el
contrario, queremos hacerlo en fase de burbujeo, le asignamos false. Este último es el
valor que recibe el parámetro por defecto.
En nuestro código de ejemplo, no le asignamos ningún valor a este parámetro y por lo tanto
se asume false, manejándose los tres en fase de burbujeo, resultando en el siguiente
orden:
1. <button>
2. <div>
3. <body>
[Link](evento, controlador,
usar_captura)
Por ejemplo:
var boton = [Link]("boton");
[Link]("click", onClick, true);
// mas tarde...
Existe la idea de que algunas de las nuevas características que introduce cada nueva versión
de ECMAScript llegan para reemplazar características anteriores. Particularmente dos de las
características de ES2015, la función flecha, y las nuevas palabras clave let y const.
Esto sería sencillamente un grave error. Tanto la función flecha, como let y const, no son
mejores que sus antecesoras, sino diferentes. Como anticipé en la introducción, en
ECMAScript lo nuevo no necesariamente viene a reemplazar a lo viejo.
En lugar de eso, debemos evitar apresurarnos a elegir la novedad sin previo análisis, y
tomarnos el tiempo para conocer con la mayor profundidad posible cada una de las nuevas
características, tomándolas como herramientas adicionales de las que disponemos para
utilizar a conciencia, y en muchas ocasiones, en conjunto con las anteriores.
let y const
Las palabras clave let y const nos sirven para declarar variables variables, similar a var,
pero con algunas diferencias importantes.
La primera diferencia radica en su ámbito de alcance. Mientras que el ámbito de las variables
declaradas con var lo define la función, el ámbito de let y const es a nivel de bloque. Esto
incluye tanto funciones, como cualquier sentencia de las que explicamos en la sección
Control de flujo:
Sentencias bloque
Sentencias condicionales
Sentencias de iteración
Sentencias switch
a; // → 1
b; // → undefined
c; // → undefined
}
miFuncion();
// → ReferenceError: b is not defined
En este fragmento, a al ser declarada con var queda disponible dentro del ámbito de la
función que la contiene, miFuncion(). Sin embargo, el alcance de b y c, al estar declaradas
con let y const,respectivamente, queda limitado al ámbito del bloque condicional if.
La segunda diferencia con var es la capacidad de ser redeclaradas. Mientras que var permite
volver a declarar variables existentes con el mismo nombre, tanto let como const solo
pueden declararse una vez.
var a = false;
let b = 1;
const c = null;
// ...
var a = true;
a; // → true
La tercer distinción tiene que ver con la posibilidad de reasignar el valor de la variable. Tanto
var como let permiten ser reasignadas a lo largo de nuestro código, es decir, pueden
cambiar su valor. Sin embargo, una variable const debe obligatoriamente recibir un valor al
ser declarada, y no podrá cambiar de valor.
var a = 1;
a = true;
a; // → true
let b = 2;
b = false;
b; // → false
const c = 3;
c = {};
// → TypeError: Assignment to constant variable.
var Función Si Si
let Bloque Si No
const Bloque No No
const no es constante
Es importante tener en cuenta que, si bien el nombre sugiere que const nos permite crear
constantes, es decir, valores inmutables, en realidad esto no es preciso.
Veamos un ejemplo:
// creamos un objeto
const objeto = { a: 1, b: 2 };
Como vemos, aunque usemos const, logramos mutar el objeto agregándole una propiedad
nueva. Con lo cual, considerar const una constante es engañoso. En lugar de eso, debemos
limitarnos a definir las variables const simplemente como variables no reasignables.
Función Flecha
La función flecha consiste en una nueva sintaxis para declarar una función.
Veamos algunos ejemplos. Estas son algunas formas de declarar una función “clásica”:
function saludar (nombre) {
return "Hola " + nombre;
}
// o bien
// o bien
Como vemos, en los casos en que la función flecha consiste en una sola sentencia, esta puede
ser declarada en una sola línea.
Por otra parte, si la función tiene un solo parámetro, los paréntesis pasan a ser opcionales.
La función flecha se vuelven muy útil para trabajar con funciones de orden superior, es decir
las que toman como parámetro una función, y/o retornan una función como resultado.
// función flecha
const sumar = a => b => c => a + b + c;
// ES5
var texto = "El " + animal + " es de color " + color;
var suma = "El resultado es " + (2 + 3);
// ES6
let texto = `El ${animal} es de color ${color}`;
let suma = `El resultado es ${2 + 3}`;
Por otra parte, a diferencia de los string ES5, las nuevas líneas son parte de la cadena de
texto.
// ES5
var texto = "Esto es un texto que se\n
extiende múltiples líneas sin\n
necesidad de caracteres especiales";
// ES6
let texto = `Esto es un texto que se
extiende múltiples líneas sin
necesidad de caracteres especiales`;
// ES5
function crearObjeto (color, ancho, alto) {
return {
color: color,
ancho: ancho,
alto: alto
};
}
// ES2015
function crearObjeto (color, ancho, alto) {
return { color, ancho, alto };
}
Desestructuración
La desestructuración (destructuring) nos permite crear variables fácilmente a partir de un
objeto.
var persona = {
nombre: "Ariel",
edad: 34,
sexo: "Masculino"
}
// ES5
var nombre = [Link];
var edad = [Link];
var sexo = [Link];
// ES2015
let { nombre, edad, sexo } = persona;
nombre; // → "Ariel"
edad; // → 34
sexo; // → "Masculina"
primero; // → "lunes"
segundo; // → "martes"
tercero; // → "miercoles"
primero; // → "lunes"
tercero; // → "miercoles"
enero; // → 31
febrero; // → 28
marzo; // → 31
abril; // → 30
Operador de Propagación
El operador de propagación (spread operator) permite expandir arrays y objetos, en lugares
donde se esperan otros valores, como pueden ser otros arrays u objetos, o argumentos de
funciones.
let array1 = [1, 2, 3];
let array2 = [...array1, 4, 5];
array2; // → [1, 2, 3, 4, 5]
let objeto1 = { x: 1, y: 2 };
let objeto2 = { ...objeto1, z: 3 };
objeto2; // → { x: 1, y: 2, z: 3 };
sumar(...array1); // → 6
// equivale a sumar(1, 2, 3);
Clonar un array
// ES5
var array2 = [Link]();
// ES6
var array2 = [...array1];
Concatenar arrays
// ES5
var array3 = [Link](array2);
// ES6
var array3 = [...array1, ...array2];
Clonar un objeto
var objeto1 = { a: 1, b: 2, c: 3 };
// ES5
function clonar (objeto) {
var copia = [Link]();
for (var propiedad in objeto) {
if ([Link](propiedad)) {
copia[propiedad] = objeto[propiedad];
}
}
return copia;
}
var objeto2 = clonar(objeto1);
// ES6
var objeto2 = { ...objeto1 };
Concatenar objetos
var objeto1 = { a: 1, b: 2 };
var objeto2 = { c: 3 };
// ES5
// Requiere crear otra función
// ES6
var objeto3 = { ...objeto1, ...objeto2 };
Parámetro Resto
El parámetro resto nos permite transformar un número indefinido de parámetros de
función en un array.
const prueba = (a, b, ...resto) => {
[Link](a);
[Link](b);
[Link](resto);
}
prueba(1, 2, 3, 4, 5);
// → 1
// → 2
// → [3, 4, 5]
function suma (a = 0, b = 0) {
return a + b;
}
suma(1, 2); // → 3 (1 + 2)
suma(1); // → 1 (1 + 0)
suma(); // → 0 (0 + 0)
suma(4, undefined); // → 4 (4 + 0)
SOBRE EL AUTOR
Ariel Fuggini nació en 1985 en la Ciudad de Buenos Aires, Argentina.
A los 15 años comenzó a programar sus primeras páginas web como hobbie. A los 21
comenzó a hacerlo en forma profesional, y a los 23 fundó su propia agencia de diseño web,
que llevo adelante por varios años.
Colaboró en cientos de proyectos digitales como desarrollador frontend, full stack, diseñador
de UI, arquitecto de infraestructura cloud, entre otros, y ha trabajado tanto con startups
como con empresas establecidas de diversos países del mundo.
Actualmente se desempeña como desarrollador full stack para una empresa unicornio basada
en San Francisco, además de mentor y creador de contenidos a través de su sitio web,
[Link].