[Resuelto] -¿Se puede modificar el valor de una variable por fuera? - JavaScript - Closures

Avatar

Estimada comunidad, estoy practicando y me he topado con esto, cuando ejecuto una función con addEventListener modifico el valor de la variable, pero solo por dentro de la función sumar(), que pasa si lo que necesito es modificar el valor de la variable en el scope global? Llevo mucho rato con este bug mental 😐, les dejo el ejercicio que aun no puedo resolver.

const next = document.getElementById('next')

let i = 0
const sumar = () => {
  i++
  console.log(i) // aquí si incrementa cada vez que pulso el botón next
}

next.addEventListener('click',  sumar)

console.log(i) // aquí no incrementa al ejecuar el addEventListener (cuando presiono el botón next)

Conviértete en premium para acceder a este video y a más de 150 cursos

Avatar

Voy a intentar que esto se vea bien, la verdad que es un autentico coñazo editar código y texto en el foro, espero que lo solucionen pronto porque es desesperante.

Vamos por partes. Tenias un error de sintaxis, pero tranquilo, nos pasa a todos, yo también soy principiante como tú y entiendo lo frustrante que puede ser cuando algo así te pasa. En tu caso, todo estaba bien, sólo que como te dije antes, la sintaxis no era la correcta. La primera parte está perfecta, me hubiera encantado ponerte el código aquí abajo, pero es imposible o yo no sé cómo hacerlo, así que lo pondré todo al final.

El error estaba cuando llamabas a la función con el click. Si mal no recuerdo, los eventos disparan una función que ejecuta lo que este dentro de ella, pero no puedes llamar a otra función directamente, utilizando el camino que planteas, si es posible hacerlo, pero de otra manera, si estoy equivocado seria genial que alguien lo comentara.

Primero te pongo tú código con la pequeña corrección y luego el otro camino para hacerlo.

let i = 0
const sumar = () => {
 i++
 console.log(i) // aquí si incrementa cada vez que pulso el botón next
}

next.addEventListener('click', e =>{
 sumar()
}) 

La otra opción

let i=0
function sumar(){
 i++
 console.log(i)
Esta opción es llamar a la función desde el HTML
<button id="next" onclick="sumar()" >Next</button>
Avatar

Hola Marlon ,gracias por responder, he pasado a código ambas instrucciones que me indicas, es cierto que el el valor de i incrementa, pero dentro del scope de la función sumar(), si hago un console.log(i) por fuera de la función (es decir por ejemplo, en la línea 13) el valor de i sigue siendo 0. De qué forma se puede hacer que el valor de i cambie por fuera en el scope global?

const next = document.getElementById('next')

let i = 0
const sumar = () => {
   i++
   console.log(i) // aquí si incrementa
}

next.addEventListener('click',  e => {
   sumar(e)
})

console.log(i) // aquí no incrementa al ejecuar el addEventListener (cuando presiono el bontón) - línea 13
Avatar

Por ejemplo si ejecuto la función sumar() por si sola el valor de i si incrementa por fuera del scope de la función, lo cual es un comportamiento normal, porque sumar() no es una función pura, eso es precisamente lo que busco que haga, pero cuando ejecuto sumar() dentro del addEventeListener no ocurre eso, es decir no altera el valor de i en el scope global.

Entiendo que al llamar a sumar() dentro del addEventLintener sumar se convierte en un callback, pero no comprendo porqué no tiene influencia en el scope global.

Avatar

Lo que entiendo es que cuando llamas a sumar en next.addEventListener('click', sumar) llama la función y el i++ no forma parte del scope global sino de la función unicamente y por eso ahi muestra el resultado en el console.log al hacer el incremento. Ahora si autoejecutas la funcion suma, ahi si hace parte del scope global y te muestra el resultado.

const next = document.getElementById('next')

let i = 0
const sumar = () => {
  i++
  console.log('dentro de la funcion: ',i) 
}

next.addEventListener('click', sumar())
next.addEventListener('click', sumar())
next.addEventListener('click', sumar())
next.addEventListener('click', sumar()) // dentro de la funcion == 4 

console.log('Fuera de la funcion:',i);//fuera de la función i == 4

//si no lo auto ejecutas
next.addEventListener('click', sumar) // dentro de la funcion i = 4  y fuera va a hacer 0
Avatar

Hola Juan Carlos, si es correcto, cuando auto ejecuto next.addEventListener('click', sumar()) el valor de i incrementa. Pero no sucede lo mismo si llamo a la función usando un botón, te muestro el código en mi codepen, cuando hago la llamada con el botón, es como si no auto ejecutara next.addEventListener('click', sumar()), o debo meter next.addEventListener('click', sumar()) dentro de otra función y hacer que esta llame al evento?

Link codepen

Avatar

Cuando haces la llamada con el boton con next.addEventListener('click', sumar()) no va a funcionar por que ya la función se autoejecutó cuando carga el DOM y aunque metas sumar() dentro de otra funcion digamos:

function sumrx(){
	sumar()
}
next.addEventListener('click', sumrx)

va a tener el mismo comportamiento que tener next.addEventListener('click', sumar)

Avatar

Vale, he estado intentando hacer lo que comentas, pero nada, así que me he puesto a buscar información y todo lo que he encontrado, indica que no se puede.

https://www.sitepoint.com/demystifying-javascript-variable-scope-hoisting/ Scoping rules vary from language to language. JavaScript has two scopes – global and local. Any variable declared outside of a function belongs to the global scope, and is therefore accessible from anywhere in your code. Each function has its own scope, and any variable declared within that function is only accessible from that function and any nested functions. Because local scope in JavaScript is created by functions, it’s also called function scope. When we put a function inside another function, then we create nested scope.

Viene a decir que cuando se declara una variable dentro de una función, dicha variable no es accesible fuera de la función.

En el caso que planteas, aunque la variable es definida fuera de la función, el incremento de la variable i++ se queda dentro de la variable y el console.log(i) sólo recoge el valor que le diste a i al inicio.

Aquí vienen a decir lo mismo https://www.w3schools.com/js/js_scope.asp JavaScript has function scope: Each function creates a new scope. Scope determines the accessibility (visibility) of these variables. Variables defined inside a function are not accessible (visible) from outside the function.

https://ultimatecourses.com/blog/everything-you-wanted-to-know-about-javascript-scope If I define a function and create variables inside it, those variables becomes locally scoped. Any locally scoped items are not visible in the global scope – unless exposed, meaning if I define functions or variables within a new scope, it’s inaccessible outside of that current scope.

Estuve viendo la los standere del ECMA pero ufffff es un documento farragoso y lo poco que puede sacar en claro es que no se puede. Ojalá alguien más arroje algo de luz sobre el tema.

Avatar

Hola Marlon, si efectivamente al parecer no se puede, estuve intentando eso porque estoy siguiendo la clase de Ajax y Web sockets (y me da vergüenza decir eso porque estando en ese curso pienso que esto ya lo debería saber... pero bueno) y lo que buscaba es cambiar el valor de offset que por defecto es 0, y al incrementarlo en 20 la URL salta a la siguiente página, devolviendo 20 nuevos personajes (esto consumido de la Api de Marvel). Entonces pensé que si declaraba una variable y luego la incrementaba con una función obtendría el resultado esperado... pero no, es por ello que hice el ejercicio que muestro al inicio de este post. Te muestro un fragmento del código del consumo de la Api de Marvel y lo que he intentado hacer, ya varios días xd (consultando código y documentación, hasta que me decidí en pedir ayuda):

const getConection = () => {
   const ts = Date.now(),
       hash = md5(ts + privateKey + publicKey)

       let page = 0 // aquí declaro la varibale que debe incrementar
       let nextPage = () => {
           page +=20
           console.log('incremento en 20 dentro de la función:', page) // aquí la variable incrementa al usar el botón next, llamado con addeventelistener
       }

       let URL = `http://gateway.marvel.com/v1/public/characters?ts=${ts}&amp;apikey=${publicKey}&amp;hash=${hash}&amp;offset=${page}` // <= este es valor offset que debe incrementar para mostrar 20 personales más o lo que lo es lo mismo, ir a la siguiente página.

       console.log('Fuera de la función', page)
       console.log(page)

       fetch(URL)
           .then(response => response.json())
           .then(response => {
               response.data.results.forEach(e => {
                   showHero(e)
                   console.log(URL)
                   
               })
           })
           .catch(e => console.log(e))

           // Llamada al botón y ejecución de la función
           next.addEventListener('click', nextPage) 
}

// Por aquía abajo ejecuto getConection y me muestra con normalidad los personajes, de la primera pagína (offset=0)

Por otro lado encontré un repositorio de github que hace algo parecido a lo que intento hacer pero con una Api de películas y noto que ha creado un objeto donde almacena un estado de página en 1, y luego con una función incrementa ese valor y manda a la url el valor aumentado... he tratado de hacerlo similar creando un objeto, pero no tengo resultados... Repositorio de github

Seguiré intentando, si encuentras algo estaría genial, gracias bro.

Avatar

Estos son los errores que tienes:

La línea donde asignas el listener debe ir fuera de la función getConnection():

next.addEventListener('click', nextPage)

La variable page debe ser de caracter global, es decir que debe ir fuera de la función getConnection(), cada vez que invocas dicha función, la variable se inicializa en 0`

La función nextPage debe estar fuera de la función getConnection.

Avatar

He aplicado lo indicado y efectivamente el valor de page incrementa, pero dentro de la función nextPage solamente, entiendo que es por scope.

Un detalle que he notado es que cuando quemo en código la ejecución de nextPage() el valor de page incrementa de forma global, incluso dentro de la función getConection, pero cuando hago la llamada a la función nextPage() dentro del addEventeListener el valor de page solo se incremente dentro de la función donde fue invocada, o sea dentro de nextPage().

Dejo el código en codepen como muestra: https://codepen.io/getbacker/pen/OJLqyrm?editors=0011

Recuerda iniciar sesión para participar en la comunidad.