José Isidro Torres Caldea
César Palma
Juan Esteban Perdomo Santos
Raycris Maldonado
Nestor Rios Garcia
Kevin Zavala
Beder Danilo Casa Condori
Orlando Rodriguez
Emerson David Cabrera Salas
Eliezer Hernandez
Kevin J. Zea Alvarado
WALT 🤖
Alejandro López Aguilar
Aranxa Martinez
José Eras
Aranxa Martinez
Larry Finol
Juan Pablo Ospina Restrepo
Humberto Asca
Stiven Andres Medina Lopez
Nestor Rios Garcia
Diego Zazueta
Luis Carlos Zapata García
Juan Reyes
Dario Paladines
Fernando Alejandro Yerena Ramos
Josué Jairo Quichca Álvarez
Oscar Barajas Tavares
Team PlatziAndres Prieto
Santiago Andres Alvarez Cuadros
Rubén Cuello
Andres Alvarez Becerra
Brandon Gomez
Tomas Pucutay
David Hereira
Eduardo Romero
Jesús León
Ismael Cruz Procel
Juan Luis Medina Coelho
silfredo mario
¡Adiós al var!
.
Con esta técnica de Closures ya nos podemos evitar totalmente el uso de la palabra reservada var, ya no necesitamos una variable que sea global para almacenar un valor que vamos a usar/modificar dentro de alguna función o bloque, ahora podemos manejar y controlar una variable que solo existe dentro de la función que la va a modificar, limitando su alcance podemos evadir errores lógicos ya que si en otra función tenemos una variable con el mismo nombre, no afectará nuestra lógica y no nos dará resultados inesperados, porque literalmente será otra variable, independiente de la que creamos en nuestra primera función. 😌
.
¿Y qué pasa si yo creo una variable dentro de una función y también la quiero usar en otra función? 😰
Tranqui... existe la palabra reservada return que nos ayudará a extraer el valor de nuestra variable y colocarla en otro contexto o alcance.
.
Con cada clase, Oscar nos va mostrando la manera correcta de programar para no caer en malas prácticas. 👨🏽🏫
"En javascript las funciones son ciudadanos de primera clase". Esta frase que la escuché hace mucho tiempo es algo que se debe tener en cuenta al crear nuestro programas.
Este es mi ejemplo. :)
const maestro = "Dios"; function crearFamilia() { const abuela = "carlina"; function hijosAbuela() { const hijoOne = "Martha"; const hijoTwo = "Evelio"; const hijoThree = "Rocio"; function hijosDeHijosAbuela() { const hijosRocio = [{ nombre: "juan" }, { nombre: "andrea" }]; const hijosMatha = [{ nombre: "Fabian" }, { nombre: "Heidy" }]; const hijosEvelio = [ { nombre: "Nathy" }, { nombre: "Juancho" }, { nombre: "Santiago" }, ]; return { generacion: function () { console.log(` ${maestro} crea a mi abuela ${abuela} la abuela tuvo 3 hijos ${hijoOne} ${hijoTwo} ${hijoThree} los hijos de la abuela tienen hijos. Rocio: ${hijosRocio[0].nombre} y ${hijosRocio[1].nombre} Martha: ${hijosMatha[0].nombre} y ${hijosMatha[1].nombre} Evelio: ${hijosEvelio[0].nombre} ${hijosEvelio[1].nombre} y ${hijosEvelio[2].nombre} `); }, }; } return hijosDeHijosAbuela(); } return hijosAbuela(); } const generar = crearFamilia(); console.log(generar.generacion());
Muy bien hecho Juan, pero para simplificar el código podrías haber hecho un map de los array de objetos y es mas fácil. ejemplo:
const maestro = "Dios"; function crearFamilia() { const abuela = "carlina"; function hijosAbuela() { const hijoOne = "Martha"; const hijoTwo = "Evelio"; const hijoThree = "Rocio"; function hijosDeHijosAbuela() { const nietos = [ { hijosRocio1: "juan", hijosRocio2: "andrea", hijosMatha1: "Fabian", hijosMatha2: "Heidy", hijosEvelio1: "Nathy", hijosEvelio2: "Juancho", hijosEvelio3: "Santiago", }, ]; return { generacion: function () { nietos.map((nieto) => { console.log( `${maestro} crea a mi abuela ${abuela} la abuela tuvo 3 hijos ${hijoOne}, ${hijoTwo}, ${hijoThree} los hijos de la abuela tienen hijos. Hijos de ${hijoThree}: ${nieto.hijosRocio1} y ${nieto.hijosRocio2}, Los hijos de ${hijoOne}: ${nieto.hijosMatha1} y ${nieto.hijosMatha2} y por ultimo tenemos a los hijos de ${hijoTwo} que son: ${nieto.hijosEvelio1}, ${nieto.hijosEvelio2} y ${nieto.hijosEvelio3}` ); }); }, }; } return hijosDeHijosAbuela(); } return hijosAbuela(); } const generar = crearFamilia(); console.log(generar.generacion());
Así sería la lógica un poquito más clara y entendible para mi, y un poco mas organizada, podría mejorar un poco más el código, pero sin embargo hiciste un buen trabajo. Saludos!
Algo revuelto, pero me encanto el ejemplo.
Les dejo este algoritmo que hace un bonito mosaico.
function mosaico() { const figuras = []; function pintar(nuevaFigura) { figuras.push(nuevaFigura); let resultado = ''; const mainLength = figuras.length * 2; for (let fila = 0; fila < mainLength; fila++) { for(let columna = 0; columna < mainLength; columna++) { const valorMax = fila > columna ? fila : columna; const valorMin = fila < columna ? fila : columna; const valorPosible = mainLength - (valorMax + 1); const indexFigura = valorPosible < valorMin ? valorPosible : valorMin; resultado += `${figuras[indexFigura]} `; } resultado += '\n'; } return resultado; } return pintar; } const m = mosaico(); console.log(m('*')); console.log(m('o')); console.log(m('+')); console.log(m('x'));
me gusto este ejercicio
Me esta costando entender mucho este concepto, pero basandome en el ejercicio de myMoneyBox, pude crear este codigo que seria ir agregando articulos a un Carrito de compras, para una tienda en especifico.
function shoppingCart(){ let cart = []; function addItems(item){ cart.push(item); console.log(cart); } return addItems; } const amazon = shoppingCart(); amazon("Mouse"); amazon("Keyboard"); amazon(`Display 24"`);
Que opinan?
Está muy bueno tu ejemplo, de hecho, me sirve para encontrarle un uso real a los closures.
Excelente!!!. Ahora podras perfeccionar la idea para conocer los productos del carrito, o incluso eliminarlos. Podrías, en lugar de retornar directamente una función, retornar un objeto con métodos, ya que estos de igual manera recordaran el lexical environment exterior y seguiría siendo un closure.
function shoppingCart(){ let cart = [] return { addItems(item){ cart.push(item); }, getCartItem(){ return cart }, removeItem(){ cart.pop() } }; } const amazon = shoppingCart(); amazon.addItems("Mouse"); amazon.addItems("Keyboard"); amazon.addItems(`Display 24"`); amazon.getCartItem() // ['Mouse', 'Keyboard', 'Display 24"'] amazon.removeItem() amazon.getCartItem() // ['Mouse', 'Keyboard']
A mí se me ocurrió este ejemplo:
.
La función moneyBox devuelve un objeto con funciones que «recuerdan» ownerName y totalMoney.
.
Además, agrego una manera en la que se pueden renombrar cada una de las funciones devueltas en el objeto retornado.
.
Espero que les sirva. 💚
excelente!
me ayudaste a conocer el concepto de sintaxis de destructuracion, muchas gracias!
¿Por qué se hace return a countCoins si es una función, y como es que al pasarle argumentos a moneyBox (el cual no tiene parametros) es como si los tomará la función countCoins?
Hace return a countCoins debido a que si es posible retornar funciones al igual que cualquier otro valor, entonces, se retorna countCoins (sin los parentesis) para que pueda ser ++invocada fuera de su contexo de ejecución++, por lo que realmente se pasa los argumentos a countCoins en lugar de a moneyBox.
const mb1 = moneyBox(); // retorna countCoins y se almacena en mb1. mb1(5); // ahora llamo countCoints que ha sido previamente almacenada en mb1 y le paso el 5 como parámetro.
Tomar en cuenta que en el return no está invocando la función, sino devolviendola para ser ejecutada fuera de su contexto de ejecución.
@jogaec Muchas gracias por tu respuesta! Me ayudaste mucho
Woou, tenía tiempo usando JavaScript de lo que había aprendido de forma autónoma, pero desde que entre a Platzi nunca paro de aprender nuevos conceptos y/o funcionalidades cómo las Closures
Quiero decir primero que todo que me ayude mucho mirando este video me ayudo a entender mas de como funcionan y como podemos implementarlos
CODE:
function moneyBox (){ let saveCoins = 0 ; return { depositar: function countCoins (coins){ saveCoins+=coins; }, retirar: function countCoins (coins){ saveCoins-=coins; }, saldo: function countCoins (){ return saveCoins } } } const moneyBoxAna = moneyBox(); console.log(moneyBoxAna.saldo()); moneyBoxAna.depositar(5); console.log(moneyBoxAna.saldo()); moneyBoxAna.retirar(1) console.log(moneyBoxAna.saldo());
RESULTADO:
Gracias por compartir el enlace del video.
Posterior a ello busqué caso típicos del uso de closure y encontré este video interesante:
3 Ejemplos REALES de Closures en JavaScript
En JavaScript las funciones se pueden comportar como objetos, entonces los closures vendria a ser un tipo de funcion que tiene estados.
Para crear un closure tenemos que crear una funcion que retorne otra funcion
Los closures era la antigua forma de simular las clases en JavaScript
Interesante información.
De hecho son dos conceptos diferentes es una combinacion de clousures con funciones tipo IIFE o funciones auto invocadas muy utilizadas para no contaminar el ambito o scope global, encapsular codigo o modularizar codigo.
Código ejemplo clase 8
function moneyBox(){ let saveCoins =0; functions countCoins(coins){ saveCoins +=coins; console.log( `MoneyBox: $${saveCoins}`) } return countCoins; } const myMoneyBox = moneyBox(); myMoneyBox(5); //ahorra 5 myMoneyBox(5); //ahorra 10 myMoneyBox(15) //ahorra 25
Mi ejemplo!
function family() { let family = []; function addMember(member) { family.push(member) console.log(`Esta familia está compuesta por ${family}`) } return addMember; } let petersFamily = family(); petersFamily('Peter'); petersFamily('Camille'); petersFamily('John'); let emilysFamily = family(); emilysFamily('Emily') emilysFamily('Carl') emilysFamily('Sophy')
La duda que me queda es porque guardamos en una variable la función global (const myMoneyBox = moneyBox()). Porqué simplemente no se ejecuta y se manda a llamar la función de forma tradicional y agregándole como parámetro las monedas => moneyBox(5). Aunque claro la función global no tiene ningún parámetro, por eso es que me surge la duda de porque agregarla a una constante y porque no sale de forma directa
Porque cuando ejecutas moneyBox() lo que estas retornando es una función, en este caso countCoins() y esta es la que recibe un parametro.
¿Por qué guardarla en una constante? Así podemos "instanciarla" varias veces y no sobreescibirla.
const myMoneyBox = moneyBox() const momMoneyBox = moneyBox() const brotherMoneyBox = moneyBox() myMoneyBox(7) // MoneyBox: 7 momMoneyBox(21) // MoneyBox: 21 brotherMoneyBox(13) // MoneyBox: 13
Cada uno mantiene su propio saveCoins.
Si lo ejecutamos directamente entonces ya no podríamos hacer más llamadas subsecuentes.
const myMoneyBox = moneyBox()(5) // MoneyBox: 13;
Si ahora intentarás guardar más monedas no se podría porque myMoneyBox nunca recibió una función.
myMoneyBox(2) // Uncaught TypeError: myMoneyBox is not a function
Y al ser una constante ya no podrías cambiar su valor.
Como es que countCoins logra obtener el parametro?? Ayuda porfavor
Cuentame mas sobre tu duda, no me queda claro.
Has este ejercicio y lo vas a entender:
function hola() { return function saluda(nombre) { console.log("Hola " + nombre) } } console.log(hola) // ⇒ Esta es una función
Acá está tu duda, sPOOiler
let saludador = hola() console.log(saludador) // ¿Qué te sale acá?
Confirmación
saludador("Platzi") // Output: // Hola Platzi
Adjunto mi aporte. Tomé la idea del carrito de compras del comentario de Ricardo Mejía y me puse a experimentar un poco con los métodos de arreglos del curso de manipulación de arrays hasta obtener el siguiente resultado:
function productos() { let agregarCarrito = []; function agregarProducto(producto) { let valorProducto = Number((Math.random()*10).toFixed(2)); agregarCarrito.push({ producto: producto, valor: valorProducto, }); } function eliminarProducto(producto){ const productoEliminado = agregarCarrito.find((value) => value.producto == producto); console.log(`Eliminaste '${productoEliminado.producto}' del carrito`); agregarCarrito.splice(agregarCarrito.findIndex(index => index.producto === producto), 1); total(); } function mostrarProductos(){ console.log(agregarCarrito.map((value) => `${value.producto}: $${value.valor}`)); } function total(){ let totalProductos = (agregarCarrito.reduce((sum, values) => sum + values.valor, 0)).toFixed(2); console.log(`El total del carrito es: $${totalProductos}`); console.log(''); } return { agregarProducto, eliminarProducto, mostrarProductos, total, }; } const carrito = productos(); carrito.agregarProducto('Galletas'); carrito.agregarProducto('Chocolate'); carrito.agregarProducto('Avena'); carrito.agregarProducto('Leche'); carrito.agregarProducto('Azúcar'); carrito.mostrarProductos(); carrito.total(); carrito.eliminarProducto('Chocolate'); carrito.eliminarProducto('Leche'); carrito.mostrarProductos(); carrito.agregarProducto('Arroz'); carrito.mostrarProductos(); carrito.total(); /*imprime: [ 'Galletas: $4.67', 'Chocolate: $1.34', 'Avena: $4.22', 'Leche: $2.76', 'Azúcar: $2.72' ] El total del carrito es: $15.71 Eliminaste 'Chocolate' del carrito El total del carrito es: $14.37 Eliminaste 'Leche' del carrito El total del carrito es: $11.61 [ 'Galletas: $4.67', 'Avena: $4.22', 'Azúcar: $2.72' ] [ 'Galletas: $4.67', 'Avena: $4.22', 'Azúcar: $2.72', 'Arroz: $7.88' ] El total del carrito es: $19.49*/
Muy ilustrativo. Siempre me pregunté como hacer para recordar valores acumulados. Ahora ya se como :smile:
Programación funcional
En este paradigma existe algo que se llama composición de funciones, closures y Currying.
Composición de funciones o funciones compuestas
Son el resultado de combinar 2 o más funciones, el resultado de cada función es el argumento de la siguiente y así sucesivamente
addOne(timesTwo(1))
Closure
Fue explicado la clase anterior
Currying
Gracias a los closures es posible implementar el Currying. Esto es descomponer funciones complejas en otras funciones más pequeñas donde cada función recibe un solo argumento.
// Sin Currying function sumThreeNumbers(a, b, c) { return a + b + c } console.log(sumThreeNumbers(1, 2, 3)) // 6 // Con Currying function sumThreeNumbers(a) { return function(b) { return function(c) { return a + b + c } } } console.log(sumThreeNumbers(1)(2)(3)) // 6
Disculpa, me suena como en el lenguaje prolog a las reglas. Es esto 🤨 o nada que ver?
Hice una modificación de prueba a la función de la clase para generar métodos específicos y así manipular las variables y protegerlas. Pueden ver un método actMoney (que muestra el dinero actual), rmvMoney (que es para retirar dinero) y addMoney (para agregar dinero).
'use strict' function moneyBox(initial){ let quantity = initial; console.log(`Your initial balance is ${quantity}`); return { addMoney: function(money){ quantity += money; console.log(`You added ${money}, your actual balance is: ${quantity}`); }, rmvMoney: function(money){ if (quantity < money) { console.log(`You can't withdraw ${money}. Not enough money`); } else { quantity -= money; console.log(`You withdrew ${money}, your actual balance is: ${quantity}`); } }, actMoney: function(){ console.log(`Your actual balance is: ${quantity}`); } } } const myMoney = moneyBox(100); myMoney.addMoney(100); myMoney.rmvMoney(200); myMoney.rmvMoney(100); myMoney.actMoney(); myMoney.addMoney(300);
No tenía idea que podia darsele este uso
Esto hace lo mismo pero con el return como una funcion llamada directamente
const moneyBox = () => { let saveCoins = 0; return (coins) => { saveCoins += coins; console.log(`MoneyBox: $${saveCoins}`); } } const myMoneyBox = moneyBox(); myMoneyBox(5); myMoneyBox(5); myMoneyBox(20); console.log('-----------------') const myMoneyBoxHector = moneyBox(); myMoneyBoxHector(10); myMoneyBoxHector(10);```
Es medio confuso el Block Scope si lo haces de manera strict y el ámbito léxico de los ++closure++. ¿Hay alguna manera de notar las diferencias para entender mejor?
Por lo general se que estamos hablando de un closure cuando tenemos 2 funciones anidadas y la función interna hace uso de variables de la externa, es decir que:
Tienes una closure cuando una función cualquiera accede a una variable fuera de su contexto.
Mientra más lo pienso menos sentido le encuentro a cómo funciona esto. No entiendo cómo logra sumar los saveCoins si literalmente la primera línea de la función está declarando e inicializando la variable a 0, ¿Cómo puede pasar eso por alto seguir sumando los valores anteriores??
O sea evidentemente pasa, el código corre y funciona perfectamente, solo me frustra no poder entender por qué 😥
Hola Juan.
Recuerda que las variables declaradas con la palabra reservada
let <code>
Sele pueden reasignar su valor. Por consiguiente ella inicia en 0, pero después sele dice que borre ese 0 y guarde un 5. Y posteriormente sele dice que ahora su valor no va a hacer 5, que en vez de eso va a hacer 5 + 10 o cualquier valor que sele este enviando.
Y esto se seguirá ejecutando cada vez que sele envíe un nuevo valor. Recuerda que cuando se usa **+= **se está diciendo que sele sume algo a lo que ya la variable tiene guardada y que ese sea su nuevo valor.
let ahorros = 5; ahorros += 10; // Si le haces un consolole.log a ahorros te arrojara 15 como resultado. <code>
Nota: Si saveCoins se hubiese hecho con la palabra reservada const, esto nunca pasaría, porque este tipo de variables, no permiten reasignar valores.