Aprovecha el precio especial

Antes:$249

Currency
$209

Paga en 4 cuotas sin intereses

Paga en 4 cuotas sin intereses
Comienza ahora

Termina en:

02d

13h

19m

31s

3

🚀 Resumen CON MARCADORES! del curso Fundamentos de JavaScript

3. Variables

5:31 Declarando una variable. var nombre.

6:21 Asignando un valor a la variable nombre. nombre = Felix

7:32 Javascript no requiere el uso de punto y coma “;” al final de cada sentencia. Por convención se decidió el uso de comillas. Hay casos muy puntuales en los cuales si se requiere el uso obligatorio de “;”. En la sección de complementos dentro de una clase está esa info.

9:19 Signo + para concatenar texto

11:11 Podemos declarar y asignar variables en la misma línea. separando con una coma “,”. Ej. var nombre = Felix, apellido = Vasquez

11:30 Javascript es un lenguaje débilmente tipado. No hay nada que especifique que una variable sea de tipo texto o número. Aquí se explica con varios ejemplos.

4. Variables: Strings

Los strings son cadenas de texto. Para indicar que estamos usando una cadena de texto debemos de colocar las comillas simples.
¿Cómo pasar un string a MAYÚSCULAS?
0:36 Invocando una función sobre una variable.

// Variables a usar en la clasevar nombre = `Felix`, Apellido = `Vasquez`;

nombreVariable.función(parámetros); // Teoría de la invocación de una función sobre una variable.
nombre.toUpperCase() // La función toUpperCase() nos ayuda a pasar el string a mayúsculas. 

¿Cómo pasar un string a minúsculas?
1:22 La función toLowerCase() nos ayuda a pasar el string a minúsculas.

nombre.toLowerCase() 

2:14¿Cómo acceder a una letra en particular del string dada su posición cómo parámetro?
.charAt(parámetro)
Ejemplo: apellido.charAt(0) nos devuelve V
Esta función requiere el paso de un parámetro para indicar la posición de la letra que queremos de vuelta. Valor numérico.

3:15¿Cómo saber cuántas letras tiene un string?
.length
length, nos indica la cantidad de caracteres que tiene un string. No invocamos paréntesis porque esto es un atributo no una función. Solo se llama al atributo length.

4:48 Concatenando strings
A la vieja escuela

var nombreCompleto = nombre + ` ` + apellido

Concatenando en los nuevos tiempos se llama Interpolación de texto

var nombreCompleto = `${nombre}${apellido}`var nombreCompleto = `${nombre}${apellido.toUpperCase()}`// Hará mayúscula el apellido

7:55¿Cómo acceder a un substring dentro de un string?

var str = nombre.charAt(1) + nombre.charAt(2) // hará el trabajo de devolver “el”, pero la siguiente es una mejor opciónvar str = nombre.substr(1, 2) //Cómo parámetro pasamos: posición y número de caracteres que queremos extraer. En este caso extrae igualmente “el”  

10:01 Desafío: Encuentra la última letra de su nombre

var nombre = `Felix`; // Definimos el nombrevar cantidadDeLetrasEnNombre = nombre.length; // Contamos cuántas letras tienevar ultimaLetraDeNombre = nombre.charAt(cantidadDeLetrasEnNombre - 1); // Devuelve “x”. Restamos 1 porque la numeración del string comienza a partir de cero.

5. Variables: Números

1:21Sumando

//Para incrementar uno podemos hacerlo de las siguientes formas
edad = edad + 1; // o
edad += 1// Ambas arrojan el mismo resultado

2:01Restando

peso = peso - 2; // o
peso -= 2// Ambas arrojan el mismo resultado

5:06Tratando decimales

var precioDeVino = 200.3; //definimos precio del vinovar total = precioDeVino * 3; //multiplicamos x 3600.9000000000001//devuelve esta locura 

La manera de almacenar decimales en JS no es tan precisa debido a que destina cierta cantidad de bytes en la ram para asignar un decimal. Esto no permite que sea preciso. Salvo que se tengan librerías externas para esto.
Para solventar este problema hay varias opciones,

var total = precioDeVino * 100 * 3 / 100;
600.9//Esto funciona porque tiene un solo decimal 

Pero si no estamos seguros de cuántos decimales son los que estamos usando en nuestro programa podemos hacer lo siguiente

var total = Math.round(precioDeVino * 100 * 3) / 100; //usando el módulo global de JS Math y lo redondeamos600.9//de esta manera va a ser un poco más preciso

Y para forzar dos decimales en el resultado a lo anterior agregamos

var totalStr = total.toFixed(2); // parámetro de la función toFixed 2 para indicar dos decimales600.90//Esto devuelve un string

Y para transformarlo nuevamente a número podemos

var total2 = parseFloat(totalStr); //es la función punto flotante y devuelve/transforma a números decimales600.90//Esto devuelve un número otra vez

10:10Dividiendo

var pizza = 8;
var personas = 2;
var cantidadDePorcionesPorPersona = pizza / personas; // Esto devuelve 4

6. Funciones

  • Las funciones son fracciones de código reutilizables. En esta clase aprenderemos a definir e invocar nuestras funciones. Para definir una función utilizaremos la palabra reservada ““function””.
  • Delimitamos el cuerpo de la función usando llaves { }. Los parámetros de la función son variables que se pasan a la función escribiendolos entre paréntesis ()
  • Definir funciones nos sirve para reutilizar código. JavaScript es un lenguaje interpretado, esto quiere decir que intentará ejecutar el código sin importar si los parámetros que le pasemos a la función estén invertidos o incluso incompletos.

0:28 Creando una función que imprime el nombre y la edad de una persona

var nombre = `Felix`, edad = 41;
functionimprimirEdad() { // No tiene definido parámetrosconsole.log(`${nombre} tiene ${edad} años`)
}
imprimirEdad()
// Esta función tal como está no acepta parámetros. Solo imprime `Felix tiene 41 años`.```js
[2:51](https://platzi.com/clases/1339-fundamentos-javascript/12889-funciones61-3/?time=171) _Agregando parámetros a la función._ Para que pueda imprimir más nombres y edades debe aceptar parámetros y para eso haremos lo siguiente:
```js
var nombre = `Felix`, edad = 41;
functionimprimirEdad(n, e) { // Agregamos los parámetros nombre y edad a la función console.log(`${n} tiene ${e} años`) // La función tiene sus propios parámetros diferentes a los de la variable inicial.
}
imprimirEdad(nombre, edad) // Si bien estos parámetros `nombre y edad` hacen referencia a los valores `Felix y 41`, cuando lo pasemos dentro de la función su nombre será `n y e`. 

De esa forma al pasarle los siguientes parámetros obtenemos lo siguiente

imprimirEdad(nombre, edad) // Devuelve `Felix tiene 41 años`
imprimirEdad(`Julia`, 28) // Devuelve `Julia tiene 28 años`
imprimirEdad(28, `Julia`) // Devuelve `28 tiene Julia años`
imprimirEdad() // Devuelve `undefined tiene undefined años`

7. El alcance de las funciones

Si una variable no está definida dentro del cuerpo de una función hablamos de una variable global. Por el contrario, una variable definida dentro de una función es una variable local.
1:51 Toda variable que no esté definida dentro de una función va a estar definida de forma global, por lo tanto puede ser accedida a través del objeto global, que en el browser es window, por lo tanto puede ser accedida de la siguiente manera window.nombreVariable

var nombre = `Felix`;
functionimprimirNombreEnMayusculas() { // No se define ningún parámetro
	nombre = nombre.toUpperCase() // Esta función devuelve un nuevo string con el string original pasado a mayúsculas. Ese string nuevo lo asignamos a la variable nombre modificando su contenido. `nombre` hace referencia a la variable externa fuera del scope y por tanto la modifica.console.log(nombre)
}
imprimirNombreEnMayusculas() // Invocamos la función sin parámetros porque no requiere ninguno
nombre // Imprime `FELIX` ya que la variable global fue modificada

Lo que está ocurriendo aquí es que la función está accediendo a la variable global.

2:51Side Effect.
Una función cómo la anterior al acceder a una variable que está fuera de su scope genera un efecto de lado (side effect).
Esto lo que significa es que al invocar la función va a generar efectos colaterales al modificar variables que no están definidas dentro de ellas, que no le pertenecen, y es algo que queremos evitar en nuestro código.

¿Cómo podemos mejorar esta función?
Para que la ejecución de una función no modifique una variable global usamos variables locales cómo parámetros en lugar de pasar directamente la variable. En la siguiente función definimos una variable local o parámetro dentro de nuestra función

var nombre = `Felix`;
functionimprimirNombreEnMayusculas(n) { // Definimos n cómo variable local y parámetro de nuestra función.
	n = n.toUpperCase()
	console.log(n)
}
imprimirNombreEnMayusculas(nombre) // Imprime `FELIX`. Invocamos la variable nombre y se la pasamos a la función cómo parámetro n
nombre // Imprime `Felix` ya que la variable global no ha sido modificada, la que se modificó fue n.
n // Imprime `Reference Error` porque no existe en el objeto global desde donde le estamos invocando

6:02 Es posible utilizar el mismo nombre para una variable global y para el parámetro de una función con un alcance local.

var nombre = `Felix`;
functionimprimirNombreEnMayusculas(nombre) { // Definimos nombre cómo variable local y parámetro de nuestra función. Esto significa que ya no podemos acceder a la variable nombre global// Desde acá adentro ya no podremos acceder a la variable nombre (global) a menos que lo hagamos de forma explícita window.nombre
	nombre = nombre.toUpperCase()
	console.log(nombre)
}
imprimirNombreEnMayusculas(nombre) // Imprime `FELIX`. Invocamos la variable nombre y se la pasamos a la función cómo parámetro nombre
nombre // Imprime `Felix` ya que invoca la variable global y esta no ha sido modificada, la que se modificó fue la variable nombre que existe dentro del scope de la función.

8. Objetos

1:35 Vamos a empezar a trabajar con objetos, veremos cómo declararlos, cuáles son sus ventajas, cómo asignarles atributos y cómo trabajar con ellos dentro de las funciones.

  • Los objetos se definen delimitados mediante llaves {}
  • Un atributo se compone de una clave (key) y un valor (value), que se separan entre sí por dos puntos “”:"". Los valores pueden ser de tipo string, número, booleano, etc. Cada atributo está separado del siguiente por una coma. Un objeto puede tener todos los atributos que sean necesarios.
  • Escribir el nombre de un objeto separado por un punto del nombre de un atributo, nos permite acceder al valor de dicho atributo para ese objeto. Un objeto también se puede pasar como atributo en una función.
  • Las últimas versiones de JavaScript nos permiten desglosar el objeto para acceder únicamente al atributo que nos interesa. Esto se consigue encerrando el nombre del atributo entre llaves { }.

Haremos una misma función de 5 formas diferentes:
4:46 La 1ra forma

var felix = {
	nombre: `Felix`, 	// clave : valor
	apellido: `Vasquez`,  // clave : valor
	edad: 28// clave : valor
};
var eduardo = {
	nombre: `Eduardo`, 	// clave : valor
	apellido: `Rodriguez`,  // clave : valor
	edad: 30// clave : valor
};
functionimprimirNombreEnMayusculas(nombre) { 	// Definimos variable local y parámetro `nombre`
nombre = nombre.toUpperCase()
	console.log(nombre)
}
imprimirNombreEnMayusculas(felix.nombre) // Accedemos al objeto `felix` y al atributo `nombre` y lo pasamos a la función
imprimirNombreEnMayusculas(eduardo.nombre)  // Imprime EDUARDO
nombre // Imprime `Reference Error` porque la variable nombre está dentro del scope de la función, y la invocamos desde un ámbito global.

5:27 2da Forma. Mejoraremos la función haciendo que reciba el objeto de la persona en vez de nombre

functionimprimirNombreEnMayusculas(persona) { 	// Definimos variable local y parámetro `persona`var nombre = persona.nombre.toUpperCase() // Creamos variable `nombre` para invocar el objeto y la clave `persona.nombre`console.log(nombre)
} // y cambiamos la forma de invocar la función a
imprimirNombreEnMayusculas(felix) // Invocamos a todo el objeto `felix` y se lo pasamos a la función
imprimirNombreEnMayusculas(eduardo)  // Imprime EDUARDO
nombre // Imprime `Reference Error` porque la variable nombre está dentro del scope de la función, y la invocamos desde un ámbito global.
persona // igual `Reference Error`

6:07 3ra Forma. Si no queremos crear una nueva variable dentro de la función.

functionimprimirNombreEnMayusculas(persona) { 	
	console.log(persona.nombre.toUpperCase()) // Insertamos la función dentro de la otra función
}
imprimirNombreEnMayusculas(felix) // Invocamos a todo el objeto `felix` y se lo pasamos a la función
imprimirNombreEnMayusculas(eduardo) // Imprime EDUARDO

6:40 4ta Forma. Desglosando el objeto dentro del parámetro de la función. Es una nueva funcioanlidad que viene con la nuevas versiones de JS.

functionimprimirNombreEnMayusculas({ nombre }) { // Colocamos los atributos del objeto que nos interesa invocarconsole.log(nombre.toUpperCase()) // Obtenemos el atributo nombre del objeto que le llegue.
}
imprimirNombreEnMayusculas(felix) // Imprime FELIX
imprimirNombreEnMayusculas(eduardo)  // Imprime EDUARDO

8:22 5ta forma. Algo más a tener en cuenta si no queremos hacer referencia a la variable que contiene nuestro objeto

imprimirNombreEnMayusculas(felix) 	// Imprime FELIX
imprimirNombreEnMayusculas(eduardo)  // Imprime EDUARDO
imprimirNombreEnMayusculas({ nombre: `Pepe` })  // Imprime PEPE
imprimirNombreEnMayusculas()  // Imprime `Reference Error`
imprimirNombreEnMayusculas({ apellido: `Moreno` })  // Imprime `Reference Error` 

IMPORTANTE: no podemos desglosar un objeto si no estamos seguros que siempre podremos pasarle el objeto que espera. Cómo vimos, en caso de pasar un valor undefined la función se daña. Y lo mismo si pasamos un atributo que no está definido en el objeto cómo el atributo apellido.

9. Desestructurar objetos

0:03 En esta clase aprenderemos otra forma de acceder a los atributos de los objetos que es la desestructurización de los mismos.
Para no duplicar las variables introducir el nombre de la variable como parámetro de la segunda variable. Ej var{nombre} = persona. Continuando con el ejemplo del punto anterior podemos expresarlo de la siguiente forma:

functionimprimirNombreEnMayusculas(persona) { // Colocamos nuevamente la variable/parámetro `persona`// var nombre = persona.nombre   (Esta sentencia es igual a la siguiente). Para evitar la duplicación de `nombre` podemos hacerlo cómo siguevar { nombre } = persona // Crea una variable con el atributo(clave) del parámetro/variable `persona`// Esta es la forma más prolija si queremos acceder al atributo de un objeto y guardarlo en una variable del mismo nombre. En otras palabras, creamos la variable `nombre` que va a salir de `persona` console.log(nombre.toUpperCase()) 
}
imprimirNombreEnMayusculas(felix) // Imprime FELIX
imprimirNombreEnMayusculas(eduardo)  // Imprime EDUARDO

1:40Reto de la clase: crea una función que imprima
Hola, me llamo Felix y tengo 28 años y
Hola, me llamo Eduardo y tengo 30 años
Usando los mismos objetos declarados en la clase procedemos así

var felix = {
	nombre: `Felix`, 	// clave : valor
	apellido: `Vasquez`,  // clave : valor
	edad: 28// clave : valor
};
var eduardo = {
	nombre: `Eduardo`, 	// clave : valor
	apellido: `Rodriguez`,  // clave : valor
	edad: 30// clave : valor
};
functionimprimirSaludo(persona) { 
	var { nombre } = persona // Extrayendo el atributo `nombre` y definiéndolo cómo variable para que console.log pueda utilizarlavar { edad } = persona // Si no definimos las variables internamente el browser arroja Errorconsole.log(`Hola, me llamo ${nombre} y tengo ${edad} años`) 
}
imprimirSaludo(felix) // Imprime `Hola, me llamo Felix y tengo 28 años`
imprimirSaludo(eduardo)  // Imprime `Hola, me llamo Eduardo y tengo 30 años`

10. Parámetros como referencia o como valor

  • Javascript se comporta de manera distinta cuando le pasamos un objeto como parámetro.
  • Cuando los objetos se pasan como parámetro (o referencia) de una función, estos se modifican fuera de la función. Para solucionar esto se puede crear un objeto diferente. Esto lo podemos hacer colocando tres puntos antes del nombre. Ej …persona.
    Comentario poderoso de Juan: Si tienes pensado usar tecnologías como Flux o Redux, pon mucha atención a esta clase. Nunca debes modificar el estado del objeto sino crear otro.

0:33 1er ejemplo. Afectación de los objetos fuera del scope de la función

functioncumpleanos(persona) { 	
	// persona.edad = persona.edad +1  (Esto equivale a lo mismo de abajo)
persona.edad += 1// Esta función incrementa en pasos de 1 la edad de persona y lo MODIFICA en el objeto original que está fuera del scope de la función
}
felix // Imprime `{nombre: "Felix", apellido: "Vasquez", edad: 28}` (objeto original)
cumpleanos(felix) // Ejecutamos la función, no hay nada que imprimir.
felix // Volvemos a imprimir el objeto `felix` y esta vez obtenemos `{nombre: "Felix", apellido: "Vasquez", edad: 29}`

😱 De esta forma se modifica el atributo edad del objeto original que está fuera de la función. Podemos decir que esta función tiene un Side Effect. OJO: esto puede ser un efecto deseado según la lógica de nuestro programa.

2:09 2do ejemplo. Evitando modificar el objeto original.

functioncumpleanos(edad) { 	// Cambiamos para que reciba cómo parámetro el atributo (o clave:valor) `edad`
	edad += 1// Esta función incrementa en pasos de 1 el atributo edad de persona y NO MODIFICA el objeto original que está fuera del scope de la función. Solo se ha pasado el atributo edad, no el objeto completo.
}
felix // Imprime `{nombre: "Felix", apellido: "Vasquez", edad: 28}`
cumpleanos(felix.edad) // Pasa el atributo/clave:valor a la función. La ejecuta, no hay nada que imprimir.
felix // Volvemos a imprimir el objeto `felix` y esta vez obtenemos `{nombre: "Felix", apellido: "Vasquez", edad: 28}`

El atributo (o clave:valor) edad del objeto no se modificó. Ya que no estamos pasando el objeto completo. Esto demuestra cómo JS se comporta diferente con los objetos, y cómo el valor objeto es pasado y recibido cómo REFERENCIA, está referenciado al objeto original, por tanto se afecta cualquier cambio que se ejecute dentro de alguna función, aunque no parezca posible debido a la naturaleza de los scopes.

3:25 3er ejemplo. Creando y devolviendo un nuevo objeto. Este objeto tendrá la misma información de la persona excepto la edad, que será 1 más.

functioncumpleanos(persona) { 
	return {			// Retornando un objeto nuevo que copia el objeto original (y sus atributos)
...persona,	// Desglosamos a `persona` dentro de este nuevo objeto
		edad: persona.edad + 1// y pisamos el atributo `edad`. Nos devuelve una persona más vieja.
}
}
felix // Imprime `{nombre: "Felix", apellido: "Vasquez", edad: 28}` (objeto original)var felixViejo = cumpleanos(felix) // Nos devuelve a `persona` con un año más `{nombre: "Felix", apellido: "Vasquez", edad: 29}`
felix // Volvemos a imprimir el objeto `felix` y obtenemos `{nombre: "Felix", apellido: "Vasquez", edad: 28}` porque no se ha modificado el objeto original.

11. Comparaciones en JavaScript

  • Existen varias maneras de comparar variables u objetos dentro de javascript. En el primer ejemplo le asignamos a X un valor numérico y a Y un string. Para poder compararlos debemos agregar dos signos de igual (==). Esto los convierte al mismo tipo de valor y permite que se puedan comparar.

  • Cuando realizamos operaciones es recomendable usar tres símbolos de igual (===). Esto permite que JavasScript no iguale las variables que son de distinto tipo. Te recomendamos que uses el triple igual siempre que estés comparando variables.

  • Existen cinco tipos de datos que son primitivos:

Boolean
Null
Undefined
Number
String

0:52Comparando dos valores (primitivos)
= sirve para asignar un valor
== Compara dos valores, sin importar el tipo de dato.
=== Compara dos valores y el tipo de dato.

x = 4, y = `4`;

x == y // Imprime `true`

x === y // Imprime `false` porque no es el mismo tipo de dato

3:26Comparando Objetos

var felix = {
	nombre: `Felix`
}
var otraPersona = {
	nombre: `Felix`
}
// Ambos objetos tienen la misma información, pero al compararlos pasa lo siguiente:
felix // imprime `Felix`
otraPersona // imprime `Felix`
felix == otra Persona // Imprime `false`.

Esto se debe a que JS compara (o pregunta) es por la referencia “variable”. Es decir compara las variables felix = otraPersona y no su contenido, y las variables no son iguales.
Para lograr que sea igual debemos hacer lo siguiente:

var otraPersona = felix
// Esto hace que ambas variables apunten a la misma referencia en memoria y por lo tanto
felix == otraPersona // Imprime `true`. Son el mismo objeto en memoria.
felix === otraPersona // Igualmente

6:16Desglosando el objeto en un objeto nuevo
Siguiendo con el ejemplo anterior

var otraPersona = {
	...felix // Creando un nuevo objeto
}
felix == otraPersona // Imprime `false`. Porque son dos objetos en posiciones de memoria diferente
felix === otraPersona // Igualmente

Para poder igualar los objetos podemos hacer lo siguiente

var otraPersona = felix
// Esto hace que ambas variables apunten a la misma referencia en memoria ram y por lo tanto
felix == otraPersona // Imprime `true`. Son el mismo objeto en memoria.
felix === otraPersona // Igualmente

7:31 ¿Qué pasa si modificamos uno de los objetos?

otraPersona.nombre = `Eduardo`
felix // Imprime `Eduardo` ya que ambos objetos apuntan a la misma posición de memoria ram.

12. Condicionales

  • En esta clase empezaremos a trabajar con estructuras de control, éstas nos permiten decidir el flujo de nuestro código.
  • Empezaremos con los condicionales. Los condicionales nos permiten decidir si un código se ejecuta o no. También introducimos un nuevo tipo de datos primitivos: el booleano, que determina si un valor es falso o verdadero. Mediante un condicional (if) decidiremos si se ejecuta una parte de nuestro código cuando se cumpla o no cierta condición.
var felix = {
	nombre: `Felix`, 	
	apellido: `Vasquez`, 
	edad: 28, 	
ingeniero: true,
cocinero: false,	
cantante: true,
};
functionimprimirProfesiones(persona) {
	console.log(`${persona.nombre} es: `)
	if (persona.ingeniero) {
		console.log(`Ingeniero`)
	} else { 
		console.log(`No es Ingeniero`)
}
	if (persona.cocinero) {
		console.log(`Cocinero`)
	} else {
		console.log(`No es Cocinero`)
	}
	if (persona.cantante) {
		console.log(`Cantante`)
	} else {
		console.log(`No es cantante`)
	}
}
imprimirProfesiones(felix) 
// imprime:
Felix es: 
Ingeniero
No es Cocinero
Cantante

7:30Desafío de la clase:
Imprimir si una persona es mayor o menor de edad.

var felix = {
	nombre: `Felix`, 	
	edad: 28, 	
};
var eduardo = {
	nombre: `Eduardo`, 	
	edad: 15, 	
};
functionimprimirSiEsMayorOMenorDeEdad(persona) {
	if (persona.edad >= 18) { // Magic Numberconsole.log(`${persona.nombre} es Mayor de edad`)
	} else {
console.log(`${persona.nombre} es Menor de edad`)
	}
}
imprimirSiEsMayorOMenorDeEdad(felix)
imprimirSiEsMayorOMenorDeEdad(eduardo)

13. Funciones que retornan valores

  • En esta clase seguiremos trabajando con condicionales para desglosar las funciones en funciones más pequeñas que retornen un valor.
  • Return detiene la ejecución de una función y devuelve el valor de esa función.
  • Las variables definidas con const se comportan como las variables, excepto que no pueden ser reasignadas. Las constantes pueden ser declaradas en mayúsculas o minúsculas. Pero por convención, para distinguirlas del resto de variables, se escribe todo en mayúsculas.
  • Vamos a mejorar el código del reto de la clase anterior. Desglosaremos la función imprimirSiEsMayorDeEdad para darle una mejor legibilidad:
const MAYORIA_DE_EDAD = 18// Definiendo esta variable evitamos el `Magic Number`. Un número que a priori nos puede confundir y requiere de un mayor esfuerzo para determinar en el contexto a que se refiere este número. Es una buena práctica.functionesMayorDeEdad(persona) { // Separamos la función que realiza la comparación de la función de impresión.return persona.edad >= MAYORIA_DE_EDAD // Esta función nos devuelve `true` o `false`
}

functionimprimirSiEsMayorOMenorDeEdad(persona) { // Queda cómo una función de impresión solamenteif (esMayorDeEdad(persona)) { // Toma el valor `true` or `false` para proseguir con la ejecuciónconsole.log(`${persona.nombre} es Mayor de edad`)
	} else {
console.log(`${persona.nombre} es Menor de edad`)
	}
}

En el libro jsparagatos.com hacen la siguiente aclaratoria:
“Hay 2 tipos principales de funciones:
-Funciones que modifican o crean valores y los retornan
-Funciones que toman valores y hacen cosas con ellos que no se pueden retornar
console.log es un ejemplo del segundo grupo: Imprime cosas en tu consola — una acción que podes ver con tus propios ojos pero que no puede ser representada como un valor de JavaScript.”
Les recomiendo su lectura como complemento a las clases.

14. Arrow functions

En esta clase aprenderemos a utilizar Arrow Functions que permiten una nomenclatura más corta para escribir expresiones de funciones. Este tipo de funciones deben definirse antes de ser utilizadas.

Al escribir las Arrow Functions no es necesario escribir la palabra function, la palabra return, ni las llaves.

0:21 Declarando la función del ejercicio anterior cómo una función anónima. Para eso la declaramos dentro de una variable, cómo sigue

var esMayorDeEdad = function (persona) { 	
return persona.edad >= MAYORIA_DE_EDAD 
}

Ahora el nombre de la función lo usamos para declarar la variable, y la función deja de tener un nombre, por eso se les llaman anónimas. La palabra function va separada del parámetro (persona).
JS es uno de los pocos lenguajes que permite declarar funciones cómo variables.

2:12 Ahora veamos cómo se hace con arrows function.

var esMayorDeEdad = persona => persona.edad >= MAYORIA_DE_EDAD // Expresión más prolija.// La siguiente es una equivalente:const esMayorDeEdad = function (persona) {
	return persona.edad >= MAYORIA_DE_EDAD
} 
// Y esta también es equivalente:const esMayorDeEdad = (persona) => {
	return persona.edad >= MAYORIA_DE_EDAD
}
// Y esta también es equivalente:const esMayorDeEdad = persona => {
	return persona.edad >= MAYORIA_DE_EDAD
}

De esta forma reducimos la expresión de 3 renglones a 1 solo renglón.
OJO: Cuando se tiene un solo parámetro dentro de la función se pueden obviar los paréntesis ().
Si una función lo único que hace es retornar algo, podemos obviar la palabra return y borrar las llaves {} que rodean el cuerpo de la función, y de forma implícita va a devolver el valor de la comparación.
5:17 Desestructurando el objeto en la función de flecha

var esMayorDeEdad = ({ edad }) => edad >= MAYORIA_DE_EDAD // Colocamos los paréntesis para poder desestructurar

De esta forma estamos diciendo que: la variable esMayorDeEdad es igual a la función anónima que tiene cómo parámetro () el atributo edad del objeto {}persona, luego toma el atributo edad y lo compara con la variable MAYORIA_DE_EDAD.
Está muy de moda escribir las funciones de esta forma.

Escribamos una función que use la comprobación de edad para permitir o no un acceso

functionpermitirAcceso(persona) {
	if (!esMayorDeEdad(persona)) { // `!` se usa para negarconsole.log(`ACCESO DENEGADO`)
	}
}

7:57 Desafío de la clase : escribe la función esMenorDeEdad en forma de arrow function y que retorne la negación de la llamada a esMayorDeEdad

const MAYORIA_DE_EDAD = 18var esMenorDeEdad = ({ edad }) => edad < MAYORIA_DE_EDAD

functionpermitirAcceso(persona) { 
	if (!esMenorDeEdad(persona)) {
	console.log(`ACCESO PERMITIDO`)
} else {
	console.log(`ACCESO DENEGADO`)
}
}

OBSERVACIÓN: Durante el ejercicio (por error) coloque cómo nombre de la función el mismo de la variable esMenorDeEdad, el resultado al ejecutar la función esMenorDeEdad(felix) es el que retorna del function arrow de la variable true o false. Al ejecutar esMenorDeEdad sin pasarle ningún parámetro retorna la función almacenada en la variable ({ edad }) => edad < MAYORIA_DE_EDAD. Para su análisis 😬

15. Estructuras repetitivas: for

  • En esta clase estudiaremos una de las estructuras básicas de control. El bucle for, se utiliza para repetir una o más instrucciones un determinado número de veces.
  • Para escribir un bucle for se coloca la palabra for seguida de paréntesis y llaves.
  • Ej. for( ){ }. Dentro de los paréntesis irán las condiciones para ejecutar el bucle, y dentro las llaves irán las instrucciones que se deben repetir.
  • En este ejemplo la variable i la utilizamos como contador.
    1:04 Creando un loop for que calcula el peso que una persona aumenta o disminuye anualmente de forma random. Para la simulación random definiremos que un 25% de las veces subirá de peso, un 25% de las veces aumentará de peso y un 50% de las veces ninguna de las dos. Para eso simularemos el transcurso de un año:
var felix = {
	nombre: `Felix`, 	
	edad: 28, 	
	peso: 75,
};
console.log(`Al inicio del año ${felix.nombre} pesa ${felix.peso}kg`) // Imprime `Al inicio del año Felix pesa 75kg`const AUMENTO_DE_PESO = 0.2// 200gramosconst DIAS_DEL_ANO = 365const aumentarPeso = persona => persona.peso += AUMENTO_DE_PESO
const adelgazar = persona => persona.peso -= AUMENTO_DE_PESO

for (var i = 1 ; i <= DIAS_DEL_ANO ; i++) { // Simulación del transcurso de un año. // `var = i` Inicializa la variable que hará de contador.// i <= 365 Compara si la variable `i` es menor o igual a 365 // i++ incrementa en 1 cada iteración del ciclo `for`. Al dejar de cumplirse la condición `i <= 365` pasa a declarar la variable random con el valor aleatorio obtenidovar random = Math.random() // Crea un número aleatorio entre 0 (incluido) y 1 (excluido) en cada una de las 365 iteraciones que pasará a la siguiente función `if` if (random < 0.25) { // Comparamos y si es menor a .25 
		aumentarPeso(felix) // Suma el valor de 200gr al peso del objeto `felix` original. Lo afecta.
} elseif (random < 0.5) { // Si es mayor a .25 y menor a .5 
adelgazar(felix) // Resta 200gr al peso del objeto `felix` original. Lo afecta
}
}
console.log(`Al final del año ${felix.nombre} pesa ${felix.peso.toFixed(1)}kg`) // Imprime `Al inicio del año Felix pesa (random value)kg`

Bonus: Agregamos la función kilosAnuales para realizar el cálculo invocando a la función y no refrescando la página. Y adicionalmente le agregamos una fecha al objeto felix para complementar el mensaje dandole más sentido con el año de la medida.

var felix = {
	nombre: `Felix`, 	
	edad: 28, 	
	peso: 75,
	ano: 2010,
};
const AUMENTO_DE_PESO = 0.2// 200gramosconst DIAS_DEL_ANO = 365const aumentarFecha = persona => persona.ano += 1// Agregamos la función que suma un año a cada iteración de 365 díasfunctionkilosAnuales(persona) {
console.log(`Al inicio del año ${persona.ano}, ${persona.nombre} pesaba ${persona.peso.toFixed(1)}kg`) 

const aumentarPeso = persona => persona.peso += AUMENTO_DE_PESO
const adelgazar = persona => persona.peso -= AUMENTO_DE_PESO

for (var i = 1 ; i <= DIAS_DEL_ANO ; i++) { 
		var random = Math.random() 
		if (random < 0.25) { 
			aumentarPeso(persona) 
} elseif (random < 0.50) { 
adelgazar(persona)
}
}
console.log(`Al final del año ${persona.ano}, ${persona.nombre} pesó ${persona.peso.toFixed(1)}kg`) 
aumentarFecha(persona)
}
kilosAnuales(felix) //Imprime// Al inicio del año 2010, Felix pesaba 82.0kg// Al final del año 2010, Felix pesó 90.4kg
kilosAnuales(felix) //Imprime// Al inicio del año 2011, Felix pesaba 90.4kg// Al final del año 2011, Felix pesó 86.7kg
kilosAnuales(felix) //Imprime// Al inicio del año 2012, Felix pesaba 86.7kg// Al final del año 2012, Felix pesó 91.5kg

16. Estructuras repetitivas: while

  • En esta clase estudiaremos otra estructura repetitiva llamada while. While se ejecuta únicamente mientras la condición que se está evaluando es verdadera.
    0:36 En el siguiente ejercicio vamos a ejemplificar con while que felix fue al nutricionista y le mandó a bajar 3kg de peso. Y evaluaremos con dos funciones la ingesta de alimentos vs. el ejercicio que realiza. Vamos a medir cuántos días le va a tomar bajar esos 3kg.
var felix = {
	nombre: `Felix`, 	
	edad: 28, 	
	peso: 75,
};
const AUMENTO_DE_PESO = 0.3// 300gramosconst DIAS_DEL_ANO = 365functionkilosPerdidos(persona) {
const META = felix.peso - 3var dias = 0const aumentarPeso = persona => persona.peso += AUMENTO_DE_PESO
const adelgazar = persona => persona.peso -= AUMENTO_DE_PESO
const comeMucho = () => Math.random() < 0.3// el 30% de las veces va a aumentar de pesoconst realizaDeporte = () => Math.random() < 0.4// el 40% de las veces va a realizar deporte para bajar de pesowhile (persona.peso > META) { // Mientras el peso de `felix` sea mayor al peso actual menos 3, decimosif (comeMucho()) { // Si `comeMucho` es menor a 0.3, es `true` y por tanto
			aumentarPeso(persona) // Ejecuta `aumentarPeso` sobre `felix`
		}
		if (realizaDeporte()) { // Si `realizaDeporte` es menor a 0.4, es `true` y por tanto
			adelgazar(persona) // Ejecuta `adelgazar` sobre `felix`
		}
		dias += 1// Suma un día por cada iteración del `while`
}
console.log(`A ${persona.nombre} le tomó ${dias} días bajar 3Kg`) 
}
kilosPerdidos(felix) // Imprime `A Felix le tomó xx días bajar 3kg`. Y cada vez que la ejecuto el objeto pierde 3kg.

Bonus: ¿Qué pasa si declaramos la variable META fuera de la función kilosPerdidos?
En el ejemplo anterior cada vez que se ejecuta la función kilosPerdidos el peso del objeto felix se ve modificado debido al Side Effect. El objeto original se ve modificado. Y por tanto la variable META, al estar referenciada al objeto original, disminuye en cada iteración 3Kg. Esto lleva a que con cada iteración el objeto vaya disminuyendo su peso hasta llegar a valores negativos, lo cual no hace mucho sentido desde el punto de vista lógico.
El Side Effect lo aprendimos durante la clase 7 Alcance de las funciones y 10 Parámetros como referencia o como valor.
Al declarar la variable META fuera de la función lo que ocurre es que al ser declarada como una variable global de tipo const esta no puede ser modificada. La variable META se declara con un valor inicial equivalente al peso del objeto felix menos 3. Eso da como resultado el valor de 72. Este valor ya no cambia a pesar que el objeto original se haya visto modificado luego de la iteración. Al iterar la función kilosPerdidos(felix) lo que observamos es que se puede iterar solo una vez como se puede apreciar:

META // Imprime 72. Al ser global ahora sí podemos consultar su valor.
felix // Imprime {nombre: "Felix", edad: 28, peso: 75}
kilosPerdidos(felix) // Imprime “A Felix le tomó xx días bajar 3Kg” la primera vez.
kilosPerdidos(felix) // Imprime “A Felix le tomó 0 días bajar 3Kg” las siguientes veces.
META // Imprime 72.
felix // Imprime {nombre: "Felix", edad: 28, peso: 71.70000000000003} Ya no se modifica más.

17. Estructuras repetitivas: do-while

Otra estructura repetitiva es el do-while. A diferencia de la instrucción while, un bucle do…while se ejecuta una vez antes de que se evalúe la expresión condicional.

functionveSiLlueve() {
var contador = 0const llueve = () => Math.random() < 0.25do {
	contador++
} while (!llueve())

console.log(`Fui a ver si llovía ${contador} veces`)
}

Reto de la clase, resuelve el bug que tiene el ejercicio. Cuando el resultado es 1 vez, entonces debemos cambiar el texto de veces a vez.

functionveSiLlueve() {
var contador = 0const llueve = () => Math.random() < 0.25do {
	contador++
} while (!llueve())
if (contador > 1) {
console.log(`Fui a ver si llovía ${contador} veces`)
} else {
	console.log(`Fui a ver si llovía ${contador} vez`)
} 
}

Otra forma es usando Operador Ternario

functionveSiLlueve() {
var contador = 0const llueve = () => Math.random() < 0.25do {
	contador++
} while (!llueve())
contador !== 1  ? console.log(`Fui a ver si llovía ${contador} veces`) : console.log(`Fui a ver si llovía ${contador} vez`)
}

18. Condicional múltiple: switch

  • Un Switch se utiliza para realizar diferentes acciones basadas en múltiples condiciones. Principalmente para evitar la anidación de muchos if.
  • Prompt, muestra un cuadro de mensaje que le pide al usuario que ingrese alguna información.
  • Break, sirve para que el browser se salte un bucle.
functionciudadFavorita() {
	var ciudad = prompt(`Escribe tu ciudad favorita de Ecuador`, `Quito`)

switch (ciudad.toLowerCase()) {
case`quito` : 
	console.log(`Es mi favorita. Me encanta su clima!`)
		breakcase`guayaquil` :
		console.log(`Su gente es lo que más me encanta!`)
		breakcase`ambato` :
		console.log(`Es hermosa, amo su tranquilidad y su Pan! 😁`)
		breakdefault :
		console.log(`No conozco esa ciudad 😞`)
		break
}
}
ciudadFavorita(QUIto) // Imprime`Es mi favorita. Me encanta su clima!`

19. Introducción a arrays

Los arrays son estructuras que nos permiten organizar elementos dentro de una colección. Estos elementos pueden ser números, strings, booleanos, objetos, etc.

var sacha = {
    nombre: 'Sacha',
    apellido: 'Lifszyc',
    altura: 1.72,
    cantidadDeLibros: 111
}
var alan = {
    nombre: 'Alan',
    apellido: 'Perez',
    altura: 1.86,
    cantidadDeLibros: 78
}
var martin = {
    nombre: 'Martin',
    apellido: 'Gomez',
    altura: 1.85,
    cantidadDeLibros: 132
}
var personas = [sacha, alan, martin]

for (var i = 0; i < personas.length; i++) {
	var persona = personas[i]
	console.log(`${persona.nombre} mide ${persona.altura}mts`)
}
// Imprime:// Sacha mide 1.72mts// Alan mide 1.86mts// Martin mide 1.85mts

Los siguientes son formas equivalentes de acceder a un atributo de un array:

personas[0].altura // Imprime 1.72
personas[0][`altura`] // Imprime 1.72

20. Filtrar un array

  • Para filtrar siempre vamos a necesitar 2 cosas, un array y una condición. En este ejemplo nuestra condición es que la estatura de las personas sea mayor de 1.80mts.
  • El método filter ( ) crea una nueva matriz con todos los elementos que pasan la prueba implementada por la función proporcionada. Recuerda que si no hay elementos que pasen la prueba, filter devuelve un array vacío.
  • En otras palabras, la función filter es la que va a iterar cada uno de los valores del array a la función esAlta, va a recorrer cada valor del array uno a uno y va a devolver (en un nuevo objeto) solo aquellos que coincidan con nuestra condición. El objeto original no es modificado.

Usando los objetos definidos en el ejercicio anterior seguimos de la siguiente manera

const esAlta = (persona) => {
	return persona.altura > 1.8
}
var personas = [ sacha, alan, martin ]
var personasAltas = personas.filter(esAlta) // `filter` nos devuelve un nuevo array, el array original no se modificaconsole.log(personasAltas)

También se puede escribir de la siguiente manera

const esAlta = persona => persona.altura > 1.8var personas = [ sacha, alan, martin ]
var personasAltas = personas.filter(esAlta) // `esAlta` es la CONDICIÓN de nuestro filtrado, declarada arriba cómo funciónconsole.log(personasAltas)

Y una forma que podemos encontrar en internet es que incluyan la función dentro del filtro, como sigue

var personas = [ sacha, alan, martin ] 
var personasAltas = personas.filter(function (persona) {
	return persona.altura > 1.8
})
console.log(personasAltas)

Y la forma más prolija que es desglosando a persona

var esAlta = ({ altura }) => altura > 1.8// No nos interesa una variable llamada `persona` sino la `altura`var personas = [ sacha, alan, martin ]
var personasAltas = personas.filter(esAlta)
console.log(personasAltas)

Desafío: escribe el filtrado de personas bajas.

var esBaja = ({ altura }) => altura <= 1.8// Cambiando el signo de comparación logramos el resultadovar personas = [ sacha, alan, martin ]
var personasBajas = personas.filter(esBaja)
console.log(personasAltas)

21. Transformar un array (con el método map())

  • En esta clase veremos cómo transformar un array. El método map() itera sobre los elementos de un array en el orden de inserción y devuelve un array nuevo con los elementos modificados.
  • Para ejemplificar
    0:50 En este ejemplo estamos modificando el atributo del objeto original.
var pasarAlturaACms = persona => {
persona.altura *= 100return persona
}
var personasCms = personas.map(pasarAlturaACms)
console.log(personasCms)

3:45 Retornando un nuevo objeto con la altura modificada

var pasarAlturaACms = persona => {
return {
...persona, // Definiendo el nuevo objeto para no modificar el objeto original
altura: persona.altura * 100// Modificando el atributo `altura` de cada objeto lo llevamos a cmts
	}
}
var personasCms = personas.map(pasarAlturaACms)
console.log(personasCms)

6:12 Agregando paréntesis al arrow function para devolver un objeto desde la función. Si el arrow function empieza con un paréntesis seguido de llaves, entonces podrá retornar el objeto que queremos. El objetivo aquí es eliminar la función return explícita.

var pasarAlturaACms = persona => ({ // Agregamos paréntesis seguido de llaves para retornar un nuevo objeto// Sin los paréntesis estaríamos poniendo el cuerpo de la función y así no es posible que nos retorne el objeto
...persona,
altura: persona.altura * 100
	})
var personasCms = personas.map(pasarAlturaACms)
console.log(personasCms)

OJO: map() devuelve un array nuevo con los criterios de filtrado que estemos aplicando, pero si en el proceso estamos modificando los atributos del objeto, estos si van a perdurar, ya que cómo objeto tratado por una función debemos recordar que está REFERENCIADO.

22. Reducir un array a un valor

El método reduce() nos permite reducir, mediante una función que se aplica a cada uno de los elemento del array, todos los elementos de dicho array, a un valor único.

var acum = 0for (var i = 0; i < personas.length; i++) {
	acum = acum + personas[i].cantidadDeLibros
}
console.log(`En total todos tienen ${acum} libros`) // Imprime `En total todos tienen 321 libros`

2:03 Ahora haremos lo mismo con reduce()

var reducer = (acum, personas) => {
return acum + personas.cantidadDeLibros
}
var totalDeLibros = personas.reduce(reducer, 0)
console.log(`En total todos tienen ${totalDeLibros} libros`) // Imprime `En total todos tienen 321 libros`

3:55 Versión más prolija del código anterior

var reducer = (acum, { cantidadDeLibros }) => acum + cantidadDeLibros
var totalDeLibros = personas.reduce(reducer, 0)
console.log(`En total todos tienen ${totalDeLibros} libros`) // Imprime `En total todos tienen 321 libros`

23. Cómo funcionan las clases en JavaScript (Prototipos/Constructores)

Las clases son funciones cuya sintaxis tiene dos componentes:

  • expresiones
  • declaraciones
    En esta clase veremos el uso de this. Dentro de una función, el valor de this depende de cómo es llamada ésta.

Crear un prototipo es muy similar a crear una variable:

  • Se antepone el keyword function;
  • La primer letra del nombre va en mayúscula;
  • Para invocar un nuevo objeto a partir de este prototipo se usa el keyword ‘new’.

Vamos a definir el prototipo de Persona cuya función será saludar:

functionPersona(nombre, apellido) { // Creamos el Prototipo Persona que actúa como FUNCIÓN CONSTRUCTORA.this.nombre = nombre // Creando el atributo `nombre`this.apellido = apellido // El keyword ‘this’ se usa para generar nuevos parámetros o atributos dentro de la declaración del objeto. 
} // Esta función no requiere `return` ya que la palabra reservada `new` que se usa en la siguiente instrucción retorna implícitamente el nuevo objeto. No se requiere hacer explícitamente.var felix = new Persona(`Felix`, `Vasquez`)

De este artículo extraemos lo siguiente para complementar la clase.
Todos los objetos dependen de un prototipo y que los prototipos son objetos, es más cualquier objeto puede ser un prototipo. De forma más concreta: un prototipo es un objeto del que otros objetos heredan propiedades.
Los objetos siempre heredan propiedades de algún objeto anterior, de este modo solo el objeto original y primigenio de javascript es el único que no hereda de nadie.
Ahora vamos a crear la función que permitirá saludar a todos los objetos creados por la función constructora.

functionPersona(nombre, apellido) {
	this.nombre = nombre
	this.apellido = apellido
}
Persona.prototype.saludar = function () { // El atributo `saludar` va a contener una función sin parámetros console.log(`Hola, me llamo ${this.nombre}${this.apellido}`) // que imprimirá el saludo.
} // Lo que hicimos fue decirle al prototipo de `Persona` que existe una atributo llamado `saludar` que contiene una función.var felix = new Persona(`Felix`, `Vasquez`)
felix.saludar() // Esta función invoca el saludo e imprime `Hola, me llamo Felix Vasquez`.
felix // Imprime el prototipo Persona con los datos del objeto felix: `Persona {nombre: “Felix”, apellido: “Vasquez”}`

Podemos crear varios objetos a partir de la función constructora:

var erika = new Persona (`Erika`, `Ruiz`)
var jessica = new Persona (`Jessica`, `Moreno`)
erika // Imprime: `Persona {nombre: “Erika”, apellido: “Ruiz”}`.
erika.saludar() //  Imprime `Hola, me llamo Erika Ruiz`.

Reto de la clase: agrega el atributo altura y la función soyAlto e imprime “soy alto” o “soy bajito” de acuerdo a si mide más de 1.8mts.

var umbral = 1.8functionPersona(nombre, apellido, altura) {
	this.nombre = nombre
	this.apellido = apellido
	this.altura = altura
}
Persona.prototype.saludar = function () { 
	if (this.altura >= umbral) {
		console.log(`Hola, me llamo ${this.nombre}${this.apellido} y soy alto`)
} else {
		console.log(`Hola, me llamo ${this.nombre}${this.apellido} y soy bajito`) 
}
}
var felix = new Persona(`Felix`, `Vasquez`, 1.8)
var jessica = new Persona(`Jessica`, `Moreno`, 1.6)
var erika = new Persona(`Erika`, `Ruiz`, 1.9)

Comentario de Dragonatack excelente complemento a la clase:
Clases en JS:
Al hablar de clases en JS, no se habla precisamente de ellas sino de prototipos, esto se debe a que no son clases como en otros lenguajes, ya que NO existe la herencia en JS .

La palabra new se utiliza para crear nuevos objetos, dado un prototipo.

Al ejecutarse la función que lleva por nombre el prototipo, implícitamente va a retornar el objeto del prototipo que acaba de construir .

24. Modificando un prototipo

En esta clase veremos cómo se modifican las clases de herencias. JavaScript funciona con una estructura orientada a objetos y cada objeto tiene una propiedad privada que mantiene un enlace a otro objeto llamado prototipo.
En esta clase seguiremos aprendiendo sobre cómo funcionan los prototipos dentro de JS y que diferencia tienen con respecto al concepto de Herencia de otros lenguajes de programación.
Es de mucha importancia donde colocamos las funciones que va a tener el prototipo. Por ello definimos los prototipos arriba, y los utilizamos a lo largo del código.

functionPersona(nombre, apellido) {
	this.nombre = nombre
	this.apellido = apellido
}
var felix = new Persona(`Felix`, `Vasquez`)
felix.saludar()
// Definir la función después de invocarla nos arrojará error.
Persona.prototype.saludar = function () { 
	console.log(`Hola, me llamo ${this.nombre}${this.apellido}`)
}

Si la función saludar() la declaramos después de la invocación de la misma el navegador arrojará error porque la función que se está invocando NO ESTÁ DEFINIDA. JavaScript detiene su ejecución en el momento que ocurre un error. Por eso nunca carga en memoria la función.

25. El contexto de las funciones: ¿Quién es this?

En esta clase explicamos por qué al introducir el arrow function salió un error. El error del contexto de this en JavaScript es uno de los errores más comunes.
Recuerda que dentro de la arrow function, this está haciendo referencia al espacio global, a window. (o quizás solo se refiere al nivel superior [el siguiente scope]). (Lexical this, permite con this llegar a un nivel superior de la función).
Ejercicio del profesor en clases:

functionPersona(nombre, apellido, altura) {
	this.nombre = nombre
	this.apellido = apellido
	this.altura = altura
}
Persona.prototype.saludar = function () { 
	console.log(`Hola, me llamo ${this.nombre}${this.apellido}`)
}
Persona.prototype.soyAlto = function () {
	returnthis.altura > 1.8
}
var felix = new Persona(`Felix`, `Vasquez`, 1.9)
var jessica = new Persona(`Jessica`, `Moreno`, 1.6)
felix.soyAlto() // Imprime `true`
jessica.soyAlto() // Imprime `false`

Modificando las funciones con Arrow functions:

functionPersona(nombre, apellido) {
	this.nombre = nombre
	this.apellido = apellido
this.altura = altura
}
Persona.prototype.saludar = function () => console.log(`Hola, me llamo ${this.nombre} ${this.apellido}`)
Persona.prototype.soyAlto = function () => this.altura > 1.8
}
varfelix = newPersona(`Felix`, `Vasquez`, 1.9)
varjessica = newPersona(`Jessica`, `Moreno`, 1.6)
felix.soyAlto() // Imprime `false` **¡¡¿¿WHAT??!!**
jessica.soyAlto() // Imprime `false`

26. La verdad oculta sobre las clases en JavaScript

Los objetos en JavaScript son “contenedores” dinámicos de propiedades. Estos objetos poseen un enlace a un objeto prototipo. Cuando intentamos acceder a la propiedad de un objeto, la propiedad no sólo se busca en el propio objeto sino también en el prototipo del objeto, en el prototipo del prototipo, y así sucesivamente hasta que se encuentre una propiedad que coincida con el nombre o se alcance el final de la cadena de prototipos.

Para esta clase continuaremos con el ejercicio anterior.
NOTA: es importante aclarar que esta clase hace referencia al PROTOTIPADO y a como se hacía la herencia antes de la solución que veremos en la próxima clase. Es más complejo, pero sin duda nos da un panorama de lo que ocurre en el método class introducido más recientemente.

functionPersona(nombre, apellido, altura) {
	this.nombre = nombre
	this.apellido = apellido
	this.altura = altura
}
Persona.prototype.saludar = function () { // Método que pertenece a la función/prototipo `Persona`.console.log(`Hola, me llamo ${this.nombre}${this.apellido}`)
}
Persona.prototype.soyAlto = function () { // Método que pertenece a la función/prototipo `Persona`.returnthis.altura > 1.8
}
functionDesarrollador(nombre, apellido, altura) { // Esta es la función que vamos a ejecutar cada vez que agreguemos nuevos desarrolladores, que a su vez son personas.this.nombre = nombre
this.apellido = apellido
this.altura = altura
}
Desarrollador.prototype.saludar = function () {  // Método que pertenece solo a la función/prototipo `Desarrollador`.console.log(`Hola, me llamo ${this.nombre}${this.apellido} y soy desarrollador`)
}

¿Cómo hacemos para que el prototipo Desarrollador “herede” los métodos del prototipo Persona.?
Para lograrlo usaremos el siguiente hack:

  1. 3:48 Crearemos una función que se encargue de “heredar” y la declaramos al inicio. function heredaDe(prototipoHijo, prototipoPadre) {}.
  2. 4:17 Esta función la vamos a invocar luego de nuestro prototipo Desarrollador ya que será la encargada de agregar los prototipos del padre al hijo.
  3. 4:40 Agregamos la siguiente función var fn = function () {} al cuerpo de la función heredaDe(). Una función vacía que no hace nada. Esta función le dice al prototipoHijo quien es su padre. En algunos lados podemos encontrar noop en lugar de fn.
  4. Verifica que estemos llamando la función heredaDe(Desarrollador, Persona) justo después de la función constructora Desarrollador y antes de cualquier método. Si ejecutamos el método saludar o cualquier otro antes de heredar los prototipos del padre entonces estaremos usando los prototipos del propio elemento.
functionheredaDe(prototipoHijo, prototipoPadre) {
	var fn = function () {} 
	fn.prototype = prototipoPadre.prototype // Asignamos al prototype de la función `fn` el prototipo del padre.
	prototipoHijo.prototype = new fn // Asignamos al prototype del prototipoHijo un nuevo objeto a partir de la función `fn`.  Básicamente `fn` actúa como intermediario para pasar los prototipos de un objeto a otro, y evitar pisar el prototype del padre.
	prototipoHijo.prototype.constructor = prototipoHijo // Asignamos la función constructora del prototipoHijo. Hay que pisar la función constructora porque de lo contrario estaríamos llamando a la función constructora del padre (Persona). Pisamos el constructor de forma similar a como hacemos en CSS.
}

functionPersona(nombre, apellido, altura) {
	this.nombre = nombre
	this.apellido = apellido
	this.altura = altura
}
Persona.prototype.saludar = function () { // Método que pertenece a la función/prototipo `Persona`.console.log(`Hola, me llamo ${this.nombre}${this.apellido}`)
}
Persona.prototype.soyAlto = function () { // Método que pertenece a la función/prototipo `Persona`.returnthis.altura > 1.8
}
functionDesarrollador(nombre, apellido, altura) { // Esta es la función que vamos a ejecutar cada vez que agreguemos nuevos desarrolladores, que a su vez son personas.this.nombre = nombre
this.apellido = apellido
this.altura = altura
}
heredaDe(Desarrollador, Persona) // Invocamos la función heredar para que el objeto Desarrollador herede los prototipos antes de pasarlo por el método que imprime el saludo de Desarrollador.

Desarrollador.prototype.saludar = function () {  // Método que pertenece solo a la función/prototipo `Desarrollador`.// De esta forma estamos pisando el atributo/función `saludar` del objeto padre, de una forma similar a como sucede en CSS.console.log(`Hola, me llamo ${this.nombre}${this.apellido} y soy desarrollador`)
}

10:25 El resultado de lo anterior se aprecia al consultar el atributo prototipo de Persona y Desarrollador

Captura de pantalla 2021-04-20 214720.png
Captura de pantalla 2021-04-20 214752.png

Esta clase me ha llevado a reflexionar sobre el parecido de “pisar” propiedades en CSS. El orden que se obtiene al “heredar” prototipos concuerda con la ejecución en cascada de CSS. El profe explica cómo el navegador recorre los atributos de un objeto hijo hacia su padre buscando el método que se haya solicitado y que solo arroja error al llegar al último y no encontrarlo. El método que definimos en nuestro constructor “pisa” al anterior. Esto se debe a que se convierte en el primero en ejecutarse cuando el navegador recorre los atributos buscando el método. Se dice que “pisa” al siguiente método declarado en la herencia (cascada) porque al encontrarlo lo ejecuta y ya deja de seguir buscando en el árbol.

Captura de pantalla 2021-04-20 225105.png

¿Cómo hago para que un prototipo herede de otro?
JS no soporta la herencia porque no soporta las clases, no hay clases, hay prototipos a los que les vamos agregando métodos que reciben funciones, saben quien es ‘this’ y saben cómo ejecutarlas. Pero no existe un sistema como tal donde yo diga: ”este prototipo va a heredar de otro”.
Lo que sí existe es la herencia prototipal.
¿Cómo funciona?
Es posible crear prototipos ‘hijo’ que van a ser un subtipo de persona en este caso, por ejemplo un ‘desarrollador’.
Este ‘hijo’, cada vez que sea requerido buscará los métodos en sí mismo, luego si no lo encuentra, en su ‘padre’, ‘abuelo’ hasta llegar al prototipo base de todos los objetos que es ‘object’. Si ‘object’ no conoce ese método, recién ahí es que javaScript lanzará el error de que ese método no puede ejecutarse.

Sin duda es complejo al principio entender los conceptos de esta clase. Pero es una gran satisfacción, luego de experimentar un poco, entender lo que sucede de fondo y desvelar las entrañas del monstruo 😂.
Acá les dejo los experimentos que hice en esta clase que me permitieron comprender el fondo del asunto:

27. Clases en JavaScript

  • Las clases de JavaScript son introducidas en el ECMAScript 2015 y son una mejora en la sintaxis sobre la herencia basada en prototipos de JavaScript.
  • La palabra clave extends se usa en declaraciones de clase o expresiones de clase para crear una clase que es hija de otra clase.
  • El método constructor es un método especial para crear e inicializar un objeto creado a partir de una clase.
  • class es una palabra reservada para especificar una definición de objetos, una especie de plantilla a usar para crear los obejtos. En esta plantilla se define cómo se crean los objetos de tipo Factura, qué propiedades tienen, cuáles son de sólo lectura, etc. Fuente
  • Las clases de javascript, introducidas en ECMAScript 2015, son una mejora sintáctica sobre la herencia basada en prototipos de JavaScript. La sintaxis de las clases no introduce un nuevo modelo de herencia orientada a objetos en JavaScript. Las clases de JavaScript proveen una sintaxis mucho más clara y simple para crear objetos y lidiar con la herencia. Fuente
  • Las clases son “funciones especiales”, como las expresiones de funciones y declaraciones de funciones, la sintaxis de una clase tiene dos componentes: expresiones de clases y declaraciones de clases.
classPersona{
constructor(nombre, apellido, altura) {
	this.nombre = nombre
	this.apellido = apellido
	this.altura = altura
}
saludar() { // No hace falta la palabra `function` para declarar la función.console.log(`Hola, me llamo ${this.nombre}${this.apellido}`)
	}
	soyAlto() { // No hace falta la palabra `function` para declarar la función.returnthis.altura > 1.8
}
}

classDesarrolladorextendsPersona{
	constructor(nombre, apellido, altura) {
		this.nombre = nombre
		this.apellido = apellido
		this.altura = altura
}
saludar() {  
		console.log(`Hola, me llamo ${this.nombre}${this.apellido} y soy desarrollador`)
}
}

Al intentar crear un objeto a partir de la función Desarrollador nos da el siguiente error:
Uncaught ReferenceError: Must call super constructor in derived class before accessing ‘this’ or returning from derived constructor

Esto se debe a que para llamar al constructor de la clase padre Persona debemos invocar super(). A partir de esta declaración ya podemos invocar a this dentro de la función. La razón es que ahora no estamos invocando new que era el encargado de:

  1. Crear un objeto JavaScript simple y en blanco;
  2. Vincularlo (o establecerlo como el constructor) de este objeto a otro objeto;
  3. Pasar el objeto recién creado del Paso 1 como el contexto this;
  4. Devuelve this si la función no devuelve un objeto.
    Fuente
Captura de pantalla 2021-04-21 182846.png

La función corregida quedaría así:

classPersona{
constructor(nombre, apellido, altura) {
	this.nombre = nombre
	this.apellido = apellido
	this.altura = altura
}
saludar() { // No hace falta la palabra `function` para declarar la función.console.log(`Hola, me llamo ${this.nombre}${this.apellido}`)
	}
	soyAlto() { // No hace falta la palabra `function` para declarar la función.returnthis.altura > 1.8
}
}

classDesarrolladorextendsPersona{
	constructor(nombre, apellido, altura) {
		super(nombre, apellido, altura)
}
saludar() {  
		console.log(`Hola, me llamo ${this.nombre}${this.apellido} y soy desarrollador`)
}
}

28. Funciones como parámetros

En JavaScript, los parámetros de funciones son por defecto undefined. De todos modos, en algunas situaciones puede ser útil colocar un valor por defecto diferente que lo evalúe como verdadero.

classPersona{
constructor(nombre, apellido, altura) {
	this.nombre = nombre
	this.apellido = apellido
	this.altura = altura
}
saludar(fn) { // Creamos el parámetro `fn` para dar una respuesta al saludoconsole.log(`Hola, me llamo ${this.nombre}${this.apellido}`)
		// Una vez que nos salude queremos saber si hay que responderle:if (fn) { // Preguntamos si hemos recibido el parámetro `responderSaludo`. Si es cierto ejecutamos la función, de lo contrario no.
		fn(this.nombre, this.apellido, false) // Toma lo nombre y apellido y define como `false` el parámetro `esDev`, ya que en la función constructora `Persona` esto debe ser falso. También se puede dejar vacío para que la función reciba `undefined`, o null, o cualquier otro valor `falsy`.
}
	}
	soyAlto() { 
	returnthis.altura > 1.8
}
}

classDesarrolladorextendsPersona{
	constructor(nombre, apellido, altura) {
		super(nombre, apellido, altura)
}
saludar(fn) {  
		console.log(`Hola, me llamo ${this.nombre}${this.apellido} y soy desarrollador/a`)
	if (fn) { // Preguntamos si hemos recibido el parámetro `responderSaludo`. Si es cierto ejecutamos la función, de lo contrario no.
		fn(this.nombre, this.apellido, true) // Toma lo nombre y apellido y define como `true` el parámetro `esDev`, ya que en la función constructora `Desarrollador` esto debe ser verdadero. También podemos usar cualquier valor truthy.
}
}
}
functionresponderSaludo(nombre, apellido, esDev) {
	console.log(`Buen día ${nombre}${apellido}`)
	if (esDev) {
		console.log(`¡Ah mirá!, no sabía que eras desarrollador/a web`)
}
}
var felix = new Persona(`Felix`, `Vasquez`, 1.9)
var erika= new Persona(`Erika`, `Ruiz`, 1.8)
var jessica = new Desarrollador(`Jessica`, `Moreno`, 1.6)

felix.saludar() // No pasamos ningún parámetro. Imprime `Hola, me llamo Felix Vasquez`.
erika.saludar(responderSaludo) // Pasamos la función responder como parámetro. Imprime `Hola, me llamo Erika Ruiz` y `Buen día Erika Ruiz`
jessica.saludar(responderSaludo) // Pasamos la función responder como parámetro. Imprime `Hola, me llamo Jessica Moreno y soy desarrollador/a` y `Buen día Jessica Moreno` y `¡Ah mirá!, no sabía que eras desarrollador/a web`.

Durante el ejercicio anterior el profesor olvidó colocar this en los elementos dentro de la función fn al llamar a nombre y apellido. Esto arrojó un error en consola. Para solucionarlo pondremos en práctica lo aprendido en clases anteriores, vamos a desestructurar el objeto que pasamos a la función fn de la siguiente manera:

saludar(fn) {  
	var nombre = this.nombre // Creamos la variable `nombre` y la igualamos al atributo `nombre` del objeto `this`.var apellido = this.apellido // Lo mismo con el apellido// La siguiente es la forma de acceder a los atributos desestructurando el objeto `this`. Ambas son válidas.// var ({ nombre, apellido }) = this console.log(`Hola, me llamo ${nombre}${apellido} y soy desarrollador/a`) // Y eliminamos el `this`.if (fn) { 
		fn(nombre, apellido, true) // Y eliminamos el `this`.
}
}

3:49 Valores falsy (valores falsos) en Javascript:

  • false
  • 0
  • null
  • “”
  • undefined
  • NaN

Los demás son valores Truthy (valores verdaderos).
Podemos ver más aquí: https://developer.mozilla.org/es/docs/Glossary/Truthy
Cuando una función no recibe un parámetro lo que se obtiene es un undefined, y eso es falso.

29. Cómo funciona el asincronismo en JavaScript

JavaScript sólo puede hacer una cosa a la vez, sin embargo; es capaz de delegar la ejecución de ciertas funciones a otros procesos. Este modelo de concurrencia se llama EventLoop.

JavaScript delega en el navegador ciertas tareas y les asocia funciones que deberán ser ejecutadas al ser completadas. Estas funciones se llaman callbacks, y una vez que el navegador ha regresado con la respuesta, el callback asociado pasa a la cola de tareas para ser ejecutado una vez que JavaScript haya terminado todas las instrucciones que están en la pila de ejecución.

Si se acumulan funciones en la cola de tareas y JavaScript se encuentra ejecutando procesos muy pesados, el EventLoop quedará bloqueado y esas funciones pudieran tardar demasiado en ejecutarse.

30. Cómo funciona el tiempo en JavaScript

En principio, cualquier tarea que se haya delegado al navegador a través de un callback, deberá esperar hasta que todas las instrucciones del programa principal se hayan ejecutado. Por esta razón el tiempo de espera definido en funciones como setTimeout, no garantizan que el callback se ejecute en ese tiempo exactamente, sino en cualquier momento a partir de allí, sólo cuando la cola de tareas se haya vaciado.

31. ¿Qué pasó con swapi.co?

SWAPI.co ha desaparecido
Desafortunadamente, swapi.co ya no se mantiene y el servicio está actualmente inactivo. El autor del proyecto, Paul Hallett, quien creó y le dio mantenimiento ha desactivado desde hace tiempo esta API que muchos utilizamos en nuestros proyectos de JavaScript para aprender a integrar un backend a un frontend.

SWAPI.dev, una nueva solución
Juriy Bura, junto a otros desarrolladores, han publicado una versión idéntica a la API utilizada por Swapi.io, la cual está disponible desde el dominio swapi.dev. Por lo tanto, de ahora en adelante, para continuar el curso sin problemas solo debes reemplazar swapi.io, la URL obliterada (la que ya no funciona), por la nueva versión que sí está disponible: swapi.dev.

Este proyecto es mantenido por la comunidad y gracias a la filosofía del código libre es posible tener una nueva versión de la API para nuestros proyectos.

Crea tu propia API de Star Wars
¿Quieres tener tu propia versión de SWAPI?

El código fuente del proyecto está disponible en GitHub, lo que te permite crear tu propia versión con solo realizar un Fork y subirlo a un servidor o consumirla en tu localhost.
Este es el repositorio del proyecto original: https://github.com/phalt/swapi.

¡Compártenos en los comentarios en link a tu repositorio fork de la API de Star Wars y qué cambios hiciste o planeas hacer en tu versión de este proyecto!

32. CallBacks

En esta clase aprenderemos qué son los callbacks y usaremos una librería externa que se llama jQuery.

Un callback es una función que se pasa a otra función como un argumento. Esta función se invoca, después, dentro de la función externa para completar alguna acción.

const API_URL = `https://swapi.co/api/`const PEOPLE_URL = `people/:id`const lukeUrl = `${API_URL}${PEOPLE_URL.replace(`:id`, 1)}`const opts = { crossDomain: true }

const onPeopleResponse = function (persona) {
	console.log(`Hola, yo soy ${persona.name}`)
}
$.get(lukeUrl, opts, onPeopleResponse)

33. Haciendo múltiples requests

En esta clase accederemos a múltiples datos al mismo tiempo. Continuaremos trabajando con los jQuery y swapi.

const API_URL = `https://swapi.co/api/`const PEOPLE_URL = `people/:id`const url = `${API_URL}${PEOPLE_URL.replace(`:id`, 1)}`const opts = { crossDomain: true } // Indica a JQuery que este request se va a hacer hacia otra página.const onPeopleResponse = function (persona) { // Función que llamaremos cuando regresemos del callback.console.log(`Hola, yo soy ${persona.name}`)
}
$.get(url, opts, onPeopleResponse) // Callback que consulta la api y llama a la función al obtener respuesta. Si no hay respuesta nunca llama a la función.

Mejorando el código y haciendo la función que invoque a la consulta al pasarle el parámetro id con el personaje que queremos consultar.

const API_URL = `https://swapi.co/api/`const PEOPLE_URL = `people/:id`const opts = { crossDomain: true } 

const onPeopleResponse = function (personaje) {
	console.log(`Hola, yo soy ${persona.name}`)
}
functionobtenerPersonaje(id) {
const url = `${API_URL}${PEOPLE_URL.replace(`:id`, id)}`
$.get(url, opts, onPeopleResponse) 
}
obtenerPersonaje(1) // Imprime `Hola yo soy C-3PO`.
obtenerPersonaje(2) // Imprime `Hola yo soy R2-D2`.
obtenerPersonaje(3) // Imprime `Hola yo soy Luke Skywalker`.

Lo que comprobamos con este ejercicio es que el orden en que fueron devueltos los personajes de star wars no fue el mismo orden en que fueron solicitados.

34. Manejando el Orden y el Asincronismo en JavaScript

Una manera de asegurar que se respete la secuencia en que hemos realizado múltiples tareas es utilizando callbacks, con lo que se ejecutará luego, en cada llamada. Lo importante es que el llamado al callback se haga a través de una función anónima. Sin embargo, al hacerlo de esta manera generamos una situación poco deseada llamada CallbackHell.

const API_URL = `https://swapi.co/api/`const PEOPLE_URL = `people/:id`const opts = { crossDomain: true } 

functionobtenerPersonaje(id, callback) { // Pasamos como parámetro la función que verifica si hay alguna función cómo parámetro. const url = `${API_URL}${PEOPLE_URL.replace(`:id`, id)}`
$.get(url, opts, function (persona) { // Movimos la función `onPeopleResponse` anónimamente console.log(`Hola, yo soy ${persona.name}`)
if (callback) { // Si el parámetro callback fue pasado a la función arroja verdadero
	callback() // y ejecuta la función callback que pasamos como argumento en la función `obtenerPersonaje`.
}
}) 
}
obtenerPersonaje(1, function () {
obtenerPersonaje(2, function () {
	obtenerPersonaje(3, function () {
	obtenerPersonaje(4, function () {
	obtenerPersonaje(5)
})
})
})
})
// Las llamadas se realizan en la secuencia indicada y las respuestas se reciben en el orden solicitado: // Imprime `Hola yo soy Luke Skywalker`.// Imprime `Hola yo soy R2-D2`.// Imprime `Hola yo soy C-3PO`.// Imprime `Hola yo soy Darth Vader`.// Imprime `Hola yo soy Leia Organa`.

El exceso de callbacks anidados se le conoce cómo CALLBACK HELL.

35. Manejo de errores con callbacks

Para solucionar el problema de quedarnos sin conexión, u otro error similar, en medio de una sucesión de callbacks utilizamos el método fail().

const API_URL = `https://swapi.co/api/`const PEOPLE_URL = `people/:id`const opts = { crossDomain: true } 

functionobtenerPersonaje(id, callback) {
const url = `${API_URL}${PEOPLE_URL.replace(`:id`, id)}`
$.get(url, opts, function (persona).fail(function () { // Encadenamos el método `fail()` para imprimir un error en caso que no se pueda terminar de ejecutar alguno de los callbacks.console.log(`Sucedió un error, No se pudo obtener el personaje ${id}`)`)
})
}
obtenerPersonaje(1, function (personaje) {
	console.log(`Hola, yo soy ${personaje.name}`)
	
obtenerPersonaje(2, function () {
console.log(`Hola, yo soy ${personaje.name}`)
	
obtenerPersonaje(3, function () {
	console.log(`Hola, yo soy ${personaje.name}`)

obtenerPersonaje(4, function () {
	console.log(`Hola, yo soy ${personaje.name}`)
	obtenerPersonaje(5)
		console.log(`Hola, yo soy ${persona.name}`)
})
})
})
})

36. Promesas

En esta clase veremos las promesas, que son valores que aún no conocemos. Las promesas tienen tres estados:

  • pending
  • fullfilled
  • rejected

Las promesas se invocan de la siguiente forma:

Promesa.png
newPromise( ( resolve, reject ) => { // Un objeto como cualquier otro de JS.// `resolve` y `reject` son funciones como parámetros que se ejecutan según la promesa se resuelve bien o mal.// --- llamado asíncronoif( todoOK ) {
     // -- se ejecutó el llamado exitosamente
     resolve()
  } else {
     // -- hubo un error en el llamado
     reject()
  }
} )

No olvides ver el material adjunto de esta clase: https://static.platzi.com/media/public/uploads/promesas_e7100aa0-540e-4d37-83fc-113b890c350e.pdf

1:06¿Qué son las Promesas?
Pensemos en ellas como valores que aún no conocemos. Es la promesa de que “ahí” habrá un valor cuando una acción asíncrona suceda y se resuelva.

Captura de pantalla 2021-04-23 092516.png
const API_URL = `https://swapi.co/api/`const PEOPLE_URL = `people/:id`const opts = { crossDomain: true } 

functionobtenerPersonaje(id) { // Ya no le pasamos la función callback.returnnewPromise((resolve, reject) => {
const url = `${API_URL}${PEOPLE_URL.replace(`:id`, id)}`
$
.get(url, opts, function (data) { // El callback ahora ejecuta la función `resolve()` con la respuesta obtenida de la consulta, es decir con el `personaje`.
resolve(data)
})
.fail(() => reject(id))
})
}

functiononError(id) {
	console.log(`Sucedió un error, No se pudo obtener el personaje ${id}`)
}

obtenerPersonaje(1) 
	.then(function(personaje) {
		console.log(`El personaje 1 es ${personaje.name}`)
})
.catch(onError) // Pasamos el nombre de la función sin los paréntesis // Imprime `El personaje 1 es Luke Skywalker`.

37. Promesas Encadenadas

A diferencia de los callbacks en el CallbackHell, que terminan estando anidados unos dentro de otros, cuando se usan Promesas la ejecución de las llamadas no se hacen de manera anidada sino de manera encadenada, al mismo nivel una debajo de la otra, lo que hace que el código sea mucho más legible y mantenible.

Captura de pantalla 2021-04-23 092701.png

Usando el ejemplo anterior:

const API_URL = `https://swapi.co/api/`const PEOPLE_URL = `people/:id`const opts = { crossDomain: true } 

functionobtenerPersonaje(id) { 
returnnewPromise((resolve, reject) => {
const url = `${API_URL}${PEOPLE_URL.replace(`:id`, id)}`
$
.get(url, opts, function (data) { 
resolve(data)
})
.fail(() => reject(id))
})
}

functiononError(id) {
	console.log(`Sucedió un error, No se pudo obtener el personaje ${id}`)
}

obtenerPersonaje(1) 
	.then(personaje => { // Cambiamos la función a Arrow function para disminuir la cantidad de texto.console.log(`El personaje 1 es ${personaje.name}`)
		return obtenerPersonaje(2) // Esta función retorna el llamado a una función que retorna una Promesa. Hasta que no se resuelva esa promesa no pasamos al siguiente `.then`. Si algún fallo ocurre la función .catch captura el error, se detiene la ejecución de los siguientes `.then` y arroja el mensaje establecido en console.log(). 
	})
.then(personaje => { 
		console.log(`El personaje 2 es ${personaje.name}`)
		return obtenerPersonaje(3)
})
.then(personaje => { 
		console.log(`El personaje 3 es ${personaje.name}`)
		return obtenerPersonaje(4)
})
.catch(onError) // Captura el error de cualquiera de las funciones `.then`. Si alguna falla este .catch captura.// Imprime `El personaje 1 es Luke Skywalker`.

38. Múltiples promesas en paralelo

Para hacer el llamado a múltiples promesas, nos apoyamos en un array de ids con el que luego construimos otro arreglo de Promesas, que pasaremos como parámetro a Promise.all( arregloDePromesas ) , con las promesas podemos encadenar llamadas en paralelo, algo que no es posible usando callbacks.

const API_URL = `https://swapi.co/api/`const PEOPLE_URL = `people/:id`const opts = { crossDomain: true } 

functionobtenerPersonaje(id) { 
returnnewPromise((resolve, reject) => {
const url = `${API_URL}${PEOPLE_URL.replace(`:id`, id)}`
$
.get(url, opts, function (data) { 
resolve(data)
})
.fail(() => reject(id))
})
}

functiononError(id) {
	console.log(`Sucedió un error, No se pudo obtener el personaje ${id}`)
}

var ids = [1, 2, 3, 4]
// var promesas  = ids.map(function (id) {//	return obtenerPersonaje(id) // })var promesas = ids.map(id => obtenerPersonaje(id)) // Forma más prolija de la función anterior.Promise.all(promesas) // Método que pertenece a la clase de promesas. Se le pasa un array de promesas.
.then(personajes => console.log(personajes))
.catch(onError)
// Imprime un array con 4 objetos. Cada objeto contiene los datos de la respuesta obtenida con el $.get// 0: {name: “Luke Skywalker”, height: “172”, mass: “77”, hair_color: “blond” ...}// 1: {name: “C-3PO”, height: “167”, mass: “75”, hair_color: “n/a” ...}// 2: {name: “R2-D2”, height: “96”, mass: “32”, hair_color: “n/a” ...}// 3: {name: “Darth Vader”, height: “202”, mass: “32”, hair_color: “none” ...}

39. Async-await: lo último en asincronismo

Async-await es la manera más simple y clara de realizar tareas asíncronas. Await detiene la ejecución del programa hasta que todas las promesas sean resueltas. Para poder utilizar esta forma, hay que colocar async antes de la definición de la función, y encerrar el llamado a Promises.all() dentro de un bloque try … catch.

const API_URL = `https://swapi.co/api/`const PEOPLE_URL = `people/:id`const opts = { crossDomain: true } 

functionobtenerPersonaje(id) { 
returnnewPromise((resolve, reject) => {
const url = `${API_URL}${PEOPLE_URL.replace(`:id`, id)}`
$
.get(url, opts, function (data) { 
resolve(data)
})
.fail(() => reject(id))
})
}

functiononError(id) {
	console.log(`Sucedió un error, No se pudo obtener el personaje ${id}`)
}

asyncfunctionobtenerPersonajes() { // Para usar `await` debemos marcar la función con `async`.var ids = [1, 2, 3, 4]
var promesas = ids.map(id => obtenerPersonaje(id)) 
try { // Todo lo que deseamos ejecutar de forma asíncrona debemos incluirlo dentro de un bloque `try/catch`.var personajes = awaitPromise.all(promesas) // Cuando todas las promesas se resuelvan guarda el resultado en `promesas`.console.log(personajes)
} catch (id) { // pasamos el `id` para luego pasarlo a la función `onError`.
	onError(id)
}
}

obtenerPersonajes()
// Imprime un array con 4 objetos. Cada objeto contiene los datos de la respuesta obtenida con el $.get// 0: {name: “Luke Skywalker”, height: “172”, mass: “77”, hair_color: “blond” ...}// 1: {name: “C-3PO”, height: “167”, mass: “75”, hair_color: “n/a” ...}// 2: {name: “R2-D2”, height: “96”, mass: “32”, hair_color: “n/a” ...}// 3: {name: “Darth Vader”, height: “202”, mass: “32”, hair_color: “none” ...}

40. Comenzando el juego “Simón dice”

Crearemos el juego Simón (Simón dice), en el que se van iluminando una secuencia de botones que el jugador tendrá que ir repitiendo, si se equivoca volverá a comenzar. El juego tendrá 10 niveles de dificultad, que deberán ser superados para ganar.

Trick:4:24 Truco para modificar varios textos iguales simultáneamente en VSCode. Seleccionas el texto y luego Ctrl+D.

// CREATE VARIABLES //const startButton = document.getElementById("startButton")
const lightBlue = document.getElementById("light-blue")
const purple = document.getElementById("purple")
const orange = document.getElementById("orange")
const green = document.getElementById("green")
 
// FUNCTION CONSTRUCTOR. This class is going to contain ALL the logic of the game //classGame{
    constructor() {
        this.initializer()
        this.sequenceGenerator()
    }
    // Constructor`s Methods
    initializer() {
        startButton.classList.add("hide") // Hide the button after starting the game.this.level = 1// Create a method in the prototype called "level". this.colors = { // Create and object with all the colors used in game, call it "colors".1: lightBlue,
            2: purple,
            3: orange,
            4: green,
        }
    }
}
 
// START GAME FUNCTION //functionstartGame() {
    var game = new Game()
}

41. Generando secuencia de números

Para generar la secuencia del juego usaremos un array con números aleatorios, que representarán el color del botón que se iluminará cada vez. Usamos new Array() para crear el arreglo de manera dinámica, y llamamos al método fill para rellenar ese array con ceros y poder luego iterar sobre éste con map().

// CREATE VARIABLES //const startButton = document.getElementById("startButton")
const lightBlue = document.getElementById("light-blue")
const purple = document.getElementById("purple")
const orange = document.getElementById("orange")
const green = document.getElementById("green")
 
// FUNCTION CONSTRUCTOR //classGame{
    constructor() {
        this.initializer()
        this.sequenceGenerator()
    }
    // Constructor`s Methods
    initializer() {
        startButton.classList.add("hide") // Hide the button after starting the game.this.level = 1// Create a method in the prototype called "level". this.colors = { // Create and object with all the colors used in game, call it "colors".1: lightBlue,
            2: purple,
            3: orange,
            4: green,
        }
    }
    sequenceGenerator() { // Creating the sequence of elements the user has to follow.this.sequence = newArray(10).fill(0).map(n => Math.floor(Math.random() * 4))
    }
}
 
// START GAME FUNCTION //functionstartGame() {
    var game = new Game()
}

Trick:3:43 para acceder al valor de una variable dentro de una función desde la consola podemos cambiar la declaración de la variable a window (objeto global) de la siguiente manera:

// AntesfunctionempezarJuego() {
	var juego = new Juego()
}
// DespuésfunctionempezarJuego() {
	window.juego = new Juego()
}
// De esta manera podemos acceder a los valores de las variables declaradas dentro de las funciones.

42. Iluminando la secuencia de colores

En esta clase se observa la diferencia entre el uso de let y var para la declaración de variables y cómo esta diferencia afecta el alcance de la variable dentro de un ciclo for. Se recomienda siempre el uso de let cuando se trata de estructuras for, ya que al usar var, el valor de dicha variable se va a reemplazar cada vez con la última asignación que se haga, mientras que con let, conservará su valor dentro de cada iteración.
Siempre que sea posible debemos usar const sobre let, y let sobre var.

// CREATE VARIABLES //const startButton = document.getElementById("startButton")
const lightBlue = document.getElementById("light-blue")
const purple = document.getElementById("purple")
const orange = document.getElementById("orange")
const green = document.getElementById("green")
 
// FUNCTION CONSTRUCTOR //classGame{
    constructor() {
        this.initializer()
        this.sequenceGenerator()
        this.nextLevel()
    }
    // Constructor`s Methods
    initializer() {
        startButton.classList.add("hide") // Hide the button after starting the game.this.level = 2// Create a method in the prototype called "level". this.colors = { // Create and object with all the colors used in game, called "colors".
            lightBlue,
            purple,
            orange,
            green,
        }
    }
    sequenceGenerator() { // Creating the sequence of elements the user has to follow.this.sequence = newArray(10).fill(0).map(n => Math.floor(Math.random() * 4))
    }
    nextLevel() {
        this.lightSequence()
      //  this.addClickEvents()
    }
    numberToColors(number) { // We pass the number through the switch to obtain the color match.switch (number) {
            case0:
                return"lightBlue"case1:
                return"purple"case2:
                return"orange"case3:
                return"green"// Don`t need a `break`. It returns the case with the true value.
        }
    }
    lightSequence() {
        for (let i = 0; i < this.level; i++) {
           // debuggerconst color = this.numberToColors(this.sequence[i]) // Create a variable with the color name.
            setTimeout(() => this.lightColor(color), 1000 * i) // Illuminate the color by 1 sec. And for each interaction add 1 more sec.
        }
    }
    lightColor(color) {
        this.colors[color].classList.add("light") // Action to add a class with 0.5 opacity to the html element.
        setTimeout(() => this.removeColor(color), 350) // Remove the class at 350ms.
    }
    removeColor(color) {
        this.colors[color].classList.remove("light")
    }
}
 
// START GAME FUNCTION //functionstartGame() {
    //debuggerwindow.game = new Game()
}

Comentario relevante que explica con un ejemplo el problema de usar var o let en un ciclo for: 1

43. Obteniendo el input del usuario

Para obtener el input del usuario agregamos un manejador para el evento click del mouse usando addEventListener para cada uno de los colores del juego. Utilizando la propiedad target devuelta por el evento click podemos identificar cuál es el botón que ha sido presionado.
Durante esta clase agregamos los siguientes códigos:

// Al   initializer()  le agregamos esta vinculación para evitar hacerla cada vez.this.chooseColor = this.chooseColor.bind(this) 

// A la función  nextLevel() agregamos el método adClickEvents.this.addClickEvents()
    }

// Agregamos la escucha de los eventos que corresponde a cada div del juego.
    addClickEvents() {
       this.colors.lightBlue.addEventListener(`click`, this.chooseColor.bind(this))              
       this.colors.purple.addEventListener(`click`, this.chooseColor)             
       this.colors.orange.addEventListener(`click`, this.chooseColor)
       this.colors.green.addEventListener(`click`, this.chooseColor)
    }
    chooseColor(ev) {
        console.log(this)
    }

Comentario de Filiberto explicando el this y recomendando una lectura que él mismo hizo:
this es el botón porque this en un addEventListener representa al elemento HTML al cual le asignamos ese evento, por ese motivo pasa de ser Game() a ser un elemento HTML.
Lo que hacemos con bind() es decirle: ¡No!, tú no serás un elemento HTML, tú serás Game().
https://filisantillan.com/this-en-diferentes-situaciones-y-su-comportamiento/

Otro comentario interesante de Ulises:
Para entender This debemos de saber que this hace referencia le bloque de código que lo ejecuta, por lo que:

functionx (){
	//this aquí hace referencia al bloque de código que es la función xfunctiony(){
		//this aquí hace referencia al bloque de código que es la función y
	}
}

Si queremos que this haga referencia al bloque de código de la función x debemos usar métodos como .bind(this)

4:11 Aquí el profesor explica el uso de .bind(this) y el ¿por qué tenemos que usarlo?. Nos dice que podemos agregar al final del método:

  addClickEvents() {
        this.colors.lightBlue.addEventListener(`click`, this.chooseColor.bind(this)) 
// La desventaja es que esto habría que repetirlo por cada elemento que escuchemos. 
    }
    chooseColor(ev) {
        console.log(this)
    }

Otra forma que podremos encontrar en muchos códigos es haciendo referencia a _this o self:

  addClickEvents() {
	  var _this = thisvar self = this// Que equivale a lo mismothis.colors.lightBlue.addEventListener(`click`, this.chooseColor.bind(_this))
        this.colors.lightBlue.addEventListener(`click`, this.chooseColor.bind(seft)) 
    }
    chooseColor(ev) {
        console.log(this)
    }

El objetivo con esto no perder la referencia del this y obtener la función que construimos para el juego en vez del elemento html.

44. Agregando la verificación del color elegido

Para obtener el input del usuario agregamos un manejador para el evento click del mouse usando addEventListener para cada uno de los colores del juego. Utilizando la propiedad target devuelta por el evento click podemos identificar cuál es el botón que ha sido presionado.

45. Agregando los estados finales del juego

Incluiremos una librería de mensajes con estilos mucho más agradables que el mensaje básico de javascript para mostrar los estados finales del juego al usuario.
Librería Sweet Alert
cdnjs
source of the library: https://cdnjs.cloudflare.com/ajax/libs/sweetalert/2.1.2/sweetalert.min.js

Introducimos la librería agregando el source al inicio de los <script> y al final del <body>.

<body></body><scriptsrc:https://cdnjs.cloudflare.com/ajax/libs/sweetalert/2.1.2/sweetalert.min.js”></script>

46. Conclusiones del curso

Así quedó el código JS del juego Simón Dice:

// CREATE VARIABLES //const startButton = document.getElementById("startButton")
const lightBlue = document.getElementById("light-blue")
const purple = document.getElementById("purple")
const orange = document.getElementById("orange")
const green = document.getElementById("green")
const LAST_LEVEL = 2// FUNCTION CONSTRUCTOR //classGame{
    constructor() {
        this.initializer()
        this.sequenceGenerator()
        this.nextLevel()
    }
    // Constructor`s Methods
    initializer() {
        this.nextLevel = this.nextLevel.bind(this)
        this.chooseColor = this.chooseColor.bind(this) // Use this to change what we get on chooseColor().this.toggleStartButton()
        startButton.classList.add("hide") // Hide the button after starting the game.this.level = 1// Create a method in the prototype called "level". this.colors = { // Create and object with all the colors used in game, called "colors".
            lightBlue,
            purple,
            orange,
            green,
        }
    }
    toggleStartButton() {
        if (startButton.classList.contains("hide")) {
            startButton.classList.remove("hide")
        } else {
            startButton.classList.add("hide")
        }
    }
    sequenceGenerator() { // Creating the sequence of elements the user has to follow.this.sequence = newArray(LAST_LEVEL).fill(0).map(n => Math.floor(Math.random() * 4))
    }
    nextLevel() {
        this.sublevel = 0// Creating this attribute to set an init level.this.lightSequence()
        this.addClickEvents()
    }
    numberToColors(number) { // Method. We pass the number through the switch to obtain the color match.switch (number) {
            case0:
                return"lightBlue"case1:
                return"purple"case2:
                return"orange"case3:
                return"green"// Don`t need a `break`. It returns the case with the true value.
        }
    }
    colorToNumbers(color) { // Method. We pass the color through the switch to obtain the number match.switch (color) {
            case"lightBlue":
                return0case"purple":
                return1case"orange":
                return2case"green":
                return3// Don`t need a `break`. It returns the case with the true value.
        }
    }
    lightSequence() {
        for (let i = 0; i < this.level; i++) {
           // debuggerconst color = this.numberToColors(this.sequence[i]) // Create a variable with the color name.
            setTimeout(() => this.lightColor(color), 1000 * i) // Illuminate the color by 1 sec. And for each interaction add 1 more sec.
        }
    }
    lightColor(color) {
        // debuggerthis.colors[color].classList.add("light") // Action to add a class with 0.5 opacity to the html element.
        setTimeout(() => this.removeColor(color), 350) // Remove the class at 350ms.
    }
    removeColor(color) {
        this.colors[color].classList.remove("light")
    }
    addClickEvents() {
        this.colors.lightBlue.addEventListener(`click`, this.chooseColor) // We could use `this.chooseColor.bind(this)` this.colors.purple.addEventListener(`click`, this.chooseColor)    // but we already declare it in initializer() to avoid to repeat it.this.colors.orange.addEventListener(`click`, this.chooseColor)
        this.colors.green.addEventListener(`click`, this.chooseColor)
    }
    removeClickEvents() {
        this.colors.lightBlue.removeEventListener(`click`, this.chooseColor) // We could use `this.chooseColor.bind(this)` this.colors.purple.removeEventListener(`click`, this.chooseColor)    // but we already declare it in initializer() to avoid to repeat it.this.colors.orange.removeEventListener(`click`, this.chooseColor)
        this.colors.green.removeEventListener(`click`, this.chooseColor)
    }
    chooseColor(ev) {
        const colorName = ev.target.dataset.color; // Capturing the color the div we clicked.const colorNumber = this.colorToNumbers(colorName); // Transforming the color into a number.this.lightColor(colorName); // Illuminating the div when click it.if (colorNumber === this.sequence[this.sublevel]) { // Verifying if the div clicked it's the right one.this.sublevel++ // If right we level up.if (this.sublevel === this.level) { // Verifying if we reach the level.this.level++ // Level up the game.this.removeClickEvents()
                // Could happen 2 things: Win the game or Level up.if (this.level === (LAST_LEVEL + 1)) { // Verifying if we reach the last level of the game.this.youWin() // You win method.
                } else { // If we don't reach the final level, we
                    setTimeout(this.nextLevel, 1500); // We set bind(this), because if we don't, this become in window and lose the reference to the game.
                }
            }
        } else {
            this.youLoss() // You loss method.
        }
    }
    youWin() {
        swal("Simon say:", "You win", "success" ) // Swal return a promise. That's why we use .then next.
        .then(() => {
            this.initializer()
            this.toggleStartButton()
        })
    }
    youLoss() {
        swal("Simon say:", "You suck", "error" ) 
        .then(() => {
            this.removeClickEvents()
            this.initializer()
            this.toggleStartButton()
        })
        
    }
}
 
// START GAME FUNCTION //functionstartGame() {
    //debuggerwindow.game = new Game()

Una alternativa más prolija podemos encontrar aquí: https://github.com/alanzzant/SimonColors
Otra opción: https://jcarlorm.github.io/platzi-game/

47. Diferencias entre var, let y const

var es la manera más antigua de declarar variables. No es muy estricta en cuanto al alcance, ya que al declarar variables de esta forma, dichas variables podrán ser accedidas, e incluso modificadas, tanto dentro como fuera de los bloques internos en una función.

Con let por otra parte, el alcance se reduce al bloque (las llaves) en el cual la variable fue declarada. Fuera de este bloque la variable no existe. Una vez declarada la variable con let, no se puede volver a declarar con en ninguna otra parte de la función.

const al igual que let se define en el contexto o alcance de un bloque, a diferencia de let y var, las variables definidas como constantes (const), ya no podrán ser modificadas ni declaradas nuevamente, en ninguna otra parte de la función o el contexto en el que ya existen.

La recomendación es reducir siempre al mínimo el alcance de nuestras variables, por lo que se debe usar let en lugar de var mientras sea posible.

48. Memoización: ahorrando cómputo

La memorización es una técnica de programación que nos permite ahorrar cómputo o procesamiento en JavaScript, al ir almacenando el resultado invariable de una función para que no sea necesario volver a ejecutar todas las instrucciones de nuevo, cuando se vuelva a llamar con los mismos parámetros. Es similar a usar memoria cache.

En la siguiente función hacemos un llamado recursivo de la función para obtener el factural de un número n. El problema con esta forma de hacerlo es que en la medida que llamamos a la función de forma recursiva vamos agregando más y más tareas al CallStack. Y el mayor problema es que cada vez que requiera el factorial de n número voy a tener que realizar el calculo, aún cuando ya lo hayamos hecho en el pasado, ya que no lo guardamops en nigún lugar. Este tipo de operación se dice que es una operación costosa desde el punto de vista del uso de los recursos computacionales.

functionfactorial(n) {
    if (n === 1) {
        return1// Cierra el ciclo recursivo y comienza la resolución de cada n factorial en la cola del callstack.
    } else {
debuggerreturn n * factorial(n - 1) // Llamado recursivo. Crea una cola en el Callstack con cada llamado.
    }
}

Para optimizar lo anterior y crear un recurso en memoria que nos ahorre el calculo cada vez podemos hacer lo siguiente:

functionfactorial(n) {
    if (!this.cache) {
        this.cache = {} // Crea un objeto llamado `cache` vacío si no existe.
    }
    if (this.cache[n]) { // Si este valor existe (si ya existe en el objeto este valor), entoncesreturnthis.cache[n] // Retorna el valor del objeto cache con la `clave: valor` correspondiente. Y listo cierra el llamado. No se ejecuta ningún calculo de factorial porque ya existe en nuestro objeto.
    }
    if (n === 1) {
        return1// Cuando n llega a 1 cierra el ciclo recursivo porque sale de la función y retorna 1. Ese 1 es entregado a la primera función en cola para su ejecución.
    } 
    debuggerthis.cache[n] = n * factorial(n - 1) // factorial(n-1) recibe valor calculado por la función y multiplica por el valor de n. factorial(n-1) crea llamado recursivo. this.cache crea el nuevo atributo `clave: valor` en el objeto cache.returnthis.cache[n] // Retorna el valor calculado arriba. Este valor retornado es entregado como parámetro a la siguiente función en espera del callstack. 
}

El último valor factorial en el callstack es el número 1, el siguiente a resolver es el 2, y así hasta llegar al valor n introducido en la función.

Para comprender mejor este proceso introduciremos un valor n = 5:

return1// factorial(n-1) retorna 1 desde la función if. Y ese valor es entregado como resultado para la siguiente función en la fila.// Ahora se ejecuta la siguiente función en espera del callstack, que tiene un valor de n=2 asignado.
cache[2] = 2 * (1) // Resuelve la multiplicación.return2// Retorna el resultado de la operación y lo pasa como parámetro a la siguiente función.
cache[3] = 3 * (2)
return6// Y lo pasa como parámetro a la siguiente función
cache[4] = 4 * (6)
return24
cache[5] = 5 * (24)
return120// Y lo imprime por cosola 
	Callstack

49. ¿Hace cuántos días naciste?

Con variables de tipo Date, se pueden realizar operaciones de suma y resta similares a las que se realizan con números. El resultado que se obtiene está en milisegundos, por lo que luego hay que hacer algunas operaciones adicionales para llevarlos a días, meses o años según queramos. También aplica para Horas, Minutos, Segundos y Milisegundos.

functiondiasEntreFechas(fecha1, fecha2) {
	const undia = 1000 * 60 * 60 * 24// Establecemos el valor de un día multiplicando 1000mseg*60seg*60min*24oras.const diferencia = Math.abs(fecha1 - fecha2) // Devuelve el valor absoluto de la resta. El módulo. elimina el signo.returnMath.floor(diferencia / undia) // Divide y redondea hacia abajo el resultado.
}
// Creamos las dos fechas que vamos a restar:const hoy = newDate()
const nacimiento = newDate(1990, 7, 12)

Comentario: También existen métodos para obtener un valor de la fecha en específico. Estos métodos son:
getFullYear()
getMonth()
getDate()
getHours()
getMinutes()
getSeconds()
getMilliseconds()
getTime()
getDay()
Date.now()

50. Funciones recursivas

La recursividad es un concepto muy importante en cualquier lenguaje de programación. Una función recursiva es básicamente aquella que se llama (o se ejecuta) a sí misma de forma controlada, hasta que sucede una condición base.
Nota: me parece que el ejemplo de la clase #48 Memoización da un ejemplo de recursividad más interesante.

functiondivisionEntera(dividendo, divisor) {
	if (dividendo < divisor) {
		return0
	}
	return1 + divisionEntera(dividendo - divisor, divisor)
}

51. Entiende los closures de JavaScript

Un closure, básicamente, es una función que recuerda el estado de las variables al momento de ser invocada, y conserva este estado a través de reiteradas ejecuciones. Un aspecto fundamental de los closures es que son funciones que retornan otras funciones.

functioncrearSaludo(finalDeFrase) {
returnfunction (nombre) {
	console.log(`Hola, soy ${nombre}${finalDeFrase}`)
}
}
const saludoArgentino = crearSaludo(`ché`)
const saludoMexicano = crearSaludo(`guey`)
const saludoColombiano = crearSaludo(`parse`)

saludoArgentino(`Sacha`) // Imprime `Hola soy Sacha ché`
saludoMexicano(`Sacha`) // Imprime `Hola soy Sacha guey`
saludoColombiano(`Sacha`) // Imprime `Hola soy Sacha parse`

52. Estructuras de datos inmutables

Las estructuras de datos inmutables forman parte de los principios de la Programación Funcional y nos permiten evitar tener efectos colaterales en los datos. En otras palabras, que hayan modificaciones en las variables sin nuestro consentimiento, produciendo comportamientos inesperados en el programa.

  • Basicamente lo que nos enseña aquí es el uso del operador spread (...) para evitar modificar el objeto original. Trabajar los nuevos objetos mientras los originales permanecen inmutables.
  • Nos permite desasernos del side effect o efecto de lado sobre los objetos que no que remos modificar.
const felix= {
	nombre: `Felix`,
	apellido: `Vasquez`,
	edad: 36
}

// const cumpleanos = persona => persona.edad++ // Esta forma modifica el objeto original.const cumpleanosInmutable = persona => ({
	...persona,
	edad: persona.edad +1
})

53. Cambiando de contexto al llamar a una función

El contexto (o alcance) de una función es por lo general, window. Así que en ciertos casos, cuando intentamos referirnos a this en alguna parte del código, es posible que tengamos un comportamiento inesperado, porque el contexto quizás no sea el que esperamos.

Existen al menos tres maneras de cambiar el contexto de una función:

  • Usando el método .bind, enviamos la referencia a la función sin ejecutarla, pasando el contexto como parámetro.
  • Usando el método .call, ejecutamos inmediatamente la función con el contexto indicado
  • Usando el método .apply, es similar a .call pero los parámetros adicionales se pasan como un arreglo de valores
const felix = {
	nombre: `Felix`,
	apellido: `Vasquez`
}
const jessica = {
	nombre: `Jessica`,
	apellido: `Moreno`
}

functionsaludar() {
	console.log(`Hola. mi nombre es ${nombre}`)
}

const saludarAFelix = saludar.bind(felix) // Imprime `Hola. mi nombre es Felix`const saludarAJessica = saludar.bind(jessica) // Imprime `Hola. mi nombre es Jessica`

Cuando usamos this en la función saludar() esta hace referencia a window, ya que es window quien esta ejecutando la función saludar() porque la tenemos definida en el scope global.

54. ¿Cuándo hace falta poner el punto y coma al final de la línea?

El punto y coma es opcional en JavaScript, excepto en algunos casos:

  • Cuando usamos varias instrucciones en una mísma línea
  • Al comenzar la próxima línea con un array
  • Al comenzar la próxima línea con un template string
why-dont-you-hit-that-like-button2.jpg
Escribe tu comentario
+ 2