Saltar al contenido

b

JavaScript

Durante el curso, tenemos el objetivo y la necesidad de aprender una cantidad suficiente de JavaScript ademas del desarrollo web.

JavaScript ha avanzado rápidamente en los últimos años y en este curso usamos características de las versiones más nuevas. El nombre oficial del estándar JavaScript es ECMAScript. En este momento, la última versión es la lanzada en junio de 2023 con el nombre ECMAScript® 2023, también conocido como ES14.

Los navegadores aún no son compatibles con todas las funciones más nuevas de JavaScript. Debido a este hecho, una gran cantidad de código que se ejecuta en los navegadores ha sido transpilado de una versión más nueva de JavaScript a una versión más antigua y compatible.

Hoy en día, la forma más popular de realizar la transpilación es mediante Babel. La transpilación se configura automáticamente en las aplicaciones de React creadas con Vite. Veremos más de cerca la configuración de la transpilación en la parte 7 de este curso.

Node.js es un entorno de ejecución de JavaScript basado en el motor de JavaScript Chrome V8 de Google y funciona prácticamente en cualquier lugar, desde servidores hasta teléfonos móviles. Practiquemos escribir algo de JavaScript usando Node. Las últimas versiones de Node ya comprenden las últimas versiones de JavaScript, por lo que no es necesario transpilar el código.

El código se escribe en archivos que terminan en .js que se ejecutan emitiendo el comando node nombre_del_archivo.js

También es posible escribir código JavaScript en la consola de Node.js, que se abre escribiendo node en la línea de comandos, así como en la consola de herramientas de desarrollo del navegador. Las revisiones más recientes de Chrome manejan las características más nuevas de JavaScript bastante bien sin transpilar el código. Alternativamente, puedes utilizar una herramienta como JS Bin.

JavaScript recuerda, tanto en nombre como en sintaxis, a Java. Pero cuando se trata del mecanismo central del lenguaje, no podrían ser más diferentes. Viniendo de un entorno de Java, el comportamiento de JavaScript puede parecer un poco extraño, especialmente si uno no hace el esfuerzo de buscar sus características.

En ciertos círculos también ha sido popular intentar "simular" características de Java y patrones de diseño en JavaScript. No recomendamos hacer esto ya que los lenguajes y los ecosistemas respectivos son, en última instancia, muy diferentes.

Variables

En JavaScript, hay algunas formas de definir las variables:

const x = 1
let y = 5

console.log(x, y)   // se imprime 1, 5
y += 10
console.log(x, y)   // se imprime 1, 15
y = 'sometext'
console.log(x, y)   // se imprime 1, sometext
x = 4               // provoca un error

const no define realmente una variable sino una constante para la cual el valor ya no se puede cambiar. Por otra parte let define una variable normal.

En el ejemplo anterior, también vemos que el tipo de datos asignados a la variable puede cambiar durante la ejecución. Al principio y almacena un número entero y al final un string.

También es posible definir variables en JavaScript usando la palabra clave var. var fue, durante mucho tiempo, la única forma de definir variables. const y let se agregaron recientemente en la versión ES6. En situaciones específicas, var funciona de una manera diferente comparada con las definiciones de variables de la mayoría de los lenguajes - mira var, let y const: ¿Cuál es la diferencia? o Diferencia entre var y let en JavaScript para más información. Durante este curso, el uso de var es desaconsejado y deberás usar const y let! Puedes encontrar más sobre este tema en YouTube, por ejemplo, var, let y const - Qué, por qué y cómo - Características de JavaScript de ES6

Arrays

Un array y un par de ejemplos de su uso:

const t = [1, -1, 3]

t.push(5)

console.log(t.length) // se imprime 4
console.log(t[1])     // se imprime -1

t.forEach(value => {
  console.log(value)  // se imprimen los números 1, -1, 3, 5 cada uno en su propia línea
})                    

En este ejemplo, cabe destacar el hecho de que el contenido de el array se puede modificar aunque esté definido como const. Como el array es un objeto, la variable siempre apunta al mismo objeto. Sin embargo, el contenido del array cambia a medida que se le agregan nuevos elementos.

Una forma de iterar a través de los elementos del array es usar forEach como se ve en el ejemplo. forEach recibe una función definida usando la sintaxis de flecha como parámetro.

value => {
  console.log(value)
}

forEach llama a la función para cada uno de los elementos del array, siempre pasando el elemento individual como parámetro. La función como parámetro de forEach también puede recibir otros parámetros.

En el ejemplo anterior, se agregó un nuevo elemento al array usando el método push. Cuando se usa React, a menudo se usan técnicas de programación funcional. Una característica del paradigma de programación funcional es el uso de estructuras de datos inmutables. En el código de React, es preferible usar el método concat, que no agrega el elemento al array, pero crea un nuevo array en la que se incluyen el contenido del array anterior y el nuevo elemento.

const t = [1, -1, 3]

const t2 = t.concat(5) // crea un nuevo array

console.log(t)  // se imprime [1, -1, 3]
console.log(t2) // se imprime [1, -1, 3, 5]

La llamada al método t.concat(5) no agrega un nuevo elemento al array anterior, pero devuelve un nuevo array que, además de contener los elementos del array anterior, también contiene el elemento nuevo.

Hay muchos métodos útiles definidos para arrays. Veamos un breve ejemplo del uso del método map.

const t = [1, 2, 3]

const m1 = t.map(value => value * 2)
console.log(m1) // se imprime [2, 4, 6]

Basado en el array anterior, map crea un nuevo array, para el cual la función dada como parámetro se usa para crear los elementos. En el caso de este ejemplo, el valor original se multiplica por dos.

Map también puede transformar el array en algo completamente diferente:

const m2 = t.map(value => '<li>' + value + '</li>')
console.log(m2)
// se imprime [ '<li>1</li>', '<li>2</li>', '<li>3</li>' ]

Aquí un array lleno de valores enteros se transforma en un array que contiene strings de HTML utilizando el método map. En la parte 2 de este curso, veremos que map se usa con bastante frecuencia en React.

Los elementos individuales de un array son fáciles de asignar a variables con la ayuda de la asignación de desestructuración.

const t = [1, 2, 3, 4, 5]

const [first, second, ...rest] = t

console.log(first, second)  // se imprime 1, 2
console.log(rest)          // se imprime [3, 4 ,5]

Gracias a la asignación, las variables first y second recibirán los dos primeros enteros del array como sus valores. Los enteros restantes se "recopilan" en un array propio que luego se asigna a la variable rest.

Objetos

Hay algunas formas diferentes de definir objetos en JavaScript. Un método muy común es usar objetos literales, que sucede al enumerar sus propiedades entre llaves:

const object1 = {
  name: 'Arto Hellas',
  age: 35,
  education: 'PhD',
}

const object2 = {
  name: 'Full Stack web application development',
  level: 'intermediate studies',
  size: 5,
}

const object3 = {
  name: {
    first: 'Dan',
    last: 'Abramov',
  },
  grades: [2, 3, 5, 3],
  department: 'Stanford University',
}

Los valores de las propiedades pueden ser de cualquier tipo, como enteros, strings, arrays, objetos...

Se hace referencia a las propiedades de un objeto usando la notación "de punto", o usando corchetes:

console.log(object1.name)         // se imprime Arto Hellas
const fieldName = 'age'
console.log(object1[fieldName])    // se imprime 35

También puedes agregar propiedades a un objeto sobre la marcha usando notación de puntos o corchetes:

object1.address = 'Helsinki'
object1['secret number'] = 12341

La última de las adiciones debe hacerse usando corchetes, porque cuando se usa la notación de puntos, secret number no es un nombre de propiedad válido debido al carácter de espacio.

Naturalmente, los objetos en JavaScript también pueden tener métodos. Sin embargo, durante este curso no es necesario definir ningún objeto con métodos propios. Es por eso que solo se discuten brevemente durante el curso.

Los objetos también se pueden definir usando las llamadas funciones de constructor, lo que da como resultado un mecanismo que recuerda a muchos otros lenguajes de programación, por ejemplo, las clases de Java. A pesar de esta similitud, JavaScript no tiene clases en el mismo sentido que los lenguajes de programación orientados a objetos. Sin embargo, ha tenido la adición de la sintaxis de clase a partir de la versión ES6, que en algunos casos ayuda a estructurar clases orientadas a objetos.

Funciones

Ya nos hemos familiarizado con la definición de funciones de flecha. El proceso completo, sin tomar atajos, para definir una función de flecha es el siguiente:

const sum = (p1, p2) => { 
  console.log (p1) 
  console.log (p2) 
  return p1 + p2 
} 

y la función se llama como se puede esperar:

const result = sum(1, 5)
console.log (result)

Si hay un solo parámetro, podemos excluir los paréntesis de la definición:

const square = p => {
  console.log(p)
  return p * p
}

Si la función solo contiene una expresión, entonces las llaves no son necesarias. En este caso, la función solo devuelve el resultado de su única expresión. Ahora, si eliminamos la impresión de la consola, podemos acortar aún más la definición de la función:

const square = p => p * p

Esta forma es particularmente útil cuando se manipulan arrays, por ejemplo, cuando se usa el método map:

const t = [1, 2, 3]
const tSquared = t.map(p => p * p)
// tSquared ahora es [1, 4, 9]

La característica de la función de flecha se agregó a JavaScript hace solo un par de años, con la versión ES6. Antes de esto, la única forma de definir funciones era usando la palabra clave function.

Hay dos formas de hacer referencia a la función; uno está dando un nombre en una declaración de función.

function product(a, b) {
  return a * b
}

const result = product(2, 6)
// result ahora es 12

La otra forma de definir la función es usando una expresión de función. En este caso, no es necesario darle un nombre a la función y la definición puede residir entre el resto del código:

const average = function(a, b) {
  return (a + b) / 2
}

const result = average(2, 5)
// result ahora es 3.5

Durante este curso definiremos todas las funciones usando la sintaxis de flecha.

Métodos de objeto y "this"

Debido al hecho de que durante este curso estamos usando una versión de React que contiene React Hooks no tenemos necesidad de definir objetos con métodos. El contenido de este capítulo no es relevante para el curso pero ciertamente, en muchos sentidos, es bueno conocerlo. En particular, cuando se utilizan versiones anteriores de React, se deben comprender los temas de este capítulo.

Las funciones de flecha y las funciones definidas usando la palabra clave function varían sustancialmente cuando se trata de cómo se comportan con respecto a la palabra clave this, que se refiere al objeto en sí.

Podemos asignar métodos a un objeto definiendo propiedades que son funciones:

const arto = {
  name: 'Arto Hellas',
  age: 35,
  education: 'PhD',
  greet: function() {    console.log('hello, my name is ' + this.name)  },}

arto.greet()  // se imprime "hello, my name is Arto Hellas"

Los métodos se pueden asignar a los objetos incluso después de la creación del objeto:

const arto = {
  name: 'Arto Hellas',
  age: 35,
  education: 'PhD',
  greet: function() {
    console.log('hello, my name is ' + this.name)
  },
}

arto.growOlder = function() {  this.age += 1}
console.log(arto.age)   // se imprime 35
arto.growOlder()
console.log(arto.age)   // se imprime 36

Modifiquemos ligeramente el objeto:

const arto = {
  name: 'Arto Hellas',
  age: 35,
  education: 'PhD',
  greet: function() {
    console.log('hello, my name is ' + this.name)
  },
  doAddition: function(a, b) {    console.log(a + b)  },}

arto.doAddition(1, 4)        // se imprime 5

const referenceToAddition = arto.doAddition
referenceToAddition(10, 15)   // se imprime 25

Ahora el objeto tiene el método doAddition que calcula la suma de números que se le da como parámetros. El método se llama de la forma habitual, utilizando el objeto arto.doAddition(1, 4) o almacenando una referencia de método en una variable y llamando al método a través de la variable: referenceToAddition(10, 15).

Si intentamos hacer lo mismo con el método greet nos encontramos con un problema:

arto.greet()       // se imprime "hello, my name is Arto Hellas"

const referenceToGreet = arto.greet
referenceToGreet() // se imprime "hello, my name is undefined"

Al llamar al método a través de una referencia, el método pierde el conocimiento de cuál era el this original. A diferencia de otros lenguajes, en JavaScript el valor de this se define en función de cómo el método se llama. Cuando se llama al método a través de una referencia, el valor de this se convierte en el llamado objeto global y el resultado final a menudo no es lo que el desarrollador de software había previsto originalmente.

Perder la pista de this al escribir código JavaScript genera algunos problemas potenciales. A menudo surgen situaciones en las que React o Node (o más específicamente el motor JavaScript del navegador web) necesita llamar a algún método en un objeto que el desarrollador ha definido. Sin embargo, en este curso evitamos estos problemas mediante el uso de JavaScript "this-less".

Una situación que lleva a la "desaparición" de this surge cuando establecemos un tiempo de espera para llamar a la función greet en el objeto arto, usando la función setTimeout.

const arto = {
  name: 'Arto Hellas',
  greet: function() {
    console.log('hello, my name is ' + this.name)
  },
}

setTimeout(arto.greet, 1000)

Como se mencionó, el valor de this en JavaScript se define en función de cómo se llama al método. Cuando setTimeout llama al método, es el motor JavaScript el que realmente llama al método y, en ese punto, this se refiere al objeto global.

Existen varios mecanismos mediante los cuales se puede conservar el this original. Uno de ellos es usar un método llamado bind:

setTimeout(arto.greet.bind(arto), 1000)

Al llamar a arto.greet.bind(arto) se crea una nueva función donde this está obligado a apuntar a Arto, independientemente de dónde y cómo se llame al método.

Usando funciones de flecha es posible resolver algunos de los problemas relacionados con this. Sin embargo, no deben usarse como métodos para objetos porque entonces this no funciona en absoluto. Más adelante volveremos al comportamiento de this en relación con las funciones de flecha.

Si deseas obtener una mejor comprensión de cómo funciona this en JavaScript, Internet está lleno de material sobre el tema, por ejemplo, la serie de screencasts Comprender la palabra clave this de JavaScript en profundidad de egghead.io es muy recomendable.

Clases

Como se mencionó anteriormente, no existe un mecanismo de clase como los de los lenguajes de programación orientados a objetos. Sin embargo, hay características en JavaScript que hacen posible "simular" clases orientadas a objetos.

Echemos un vistazo rápido a la sintaxis de clase que se introdujo en JavaScript con ES6, que simplifica sustancialmente la definición de clases (o cosas similares a clases) en JavaScript.

En el siguiente ejemplo definimos una "clase" llamada Person y dos objetos Person:

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  greet() {
    console.log('hello, my name is ' + this.name)
  }
}

const adam = new Person('Adam Ondra', 29)
adam.greet()

const janja = new Person('Janja Garnbret', 23)
janja.greet()

Cuando se trata de sintaxis, las clases y los objetos creados a partir de ellos recuerdan mucho a las clases y objetos de Java. Su comportamiento también es bastante similar al de los objetos Java. En el núcleo, siguen siendo objetos basados en la herencia prototípica de JavaScript. El tipo de ambos objetos es en realidad Object, ya que JavaScript esencialmente solo define los tipos Boolean, Null, Undefined, Number, String, Symbol, BigInt y Object.

La introducción de la sintaxis de clases fue una adición controvertida. Consulta No es impresionante: clases de ES6 o ¿Es la "clase" en ES6 la nueva parte "mala"? para obtener más detalles.

La sintaxis de la clase ES6 se usa mucho en React "antiguo" y también en Node.js, por lo que comprenderlo es beneficioso incluso en este curso. Sin embargo, dado que estamos usando la nueva función Hooks de React a lo largo de este curso, no tenemos un uso concreto para la sintaxis de clases de JavaScripts.

Materiales JavaScript

Existen guías buenas y malas para JavaScript en Internet. La mayoría de los enlaces en esta página relacionados con características de JavaScript se refieren a la Guía de JavaScript de Mozilla.

Te recomendamos leer inmediatamente Una re-introducción a JavaScript (tutorial de JS) en el sitio web de Mozilla.

Si deseas conocer JavaScript en profundidad, hay una gran serie de libros gratuitos en Internet llamada You-Dont-Know-JS.

Otro gran recurso para aprender JavaScript es javascript.info.

El libro gratuito Eloquent JavaScript te lleva desde los conceptos básicos hasta temas interesantes rápidamente. Es una mezcla de teoría, proyectos y ejercicios, y abarca tanto la teoría general de programación como el lenguaje JavaScript.

Namaste 🙏 JavaScript es otro excelente y altamente recomendado tutorial gratuito de JavaScript para entender cómo funciona JS bajo el capó. Namaste JavaScript es un curso puro y en profundidad de JavaScript lanzado de forma gratuita en YouTube. Cubrirá en detalle los conceptos fundamentales de JavaScript y todo acerca de cómo JS funciona detrás de escena dentro del motor de JavaScript.

egghead.io tiene muchos screencasts de calidad sobre JavaScript, React y otros temas interesantes. Desafortunadamente, parte del material está detrás de un muro de pago.