Scope and IIFE in JS [duplicate]

3

Are the staff all right? I'm here with a question in a small code, basically I have a for loop that adds a click event to each number where the number that was clicked on the alert will be displayed, and of course the number presented is always the last, until I realized it, but I have a problem understanding the scope that was added to the code, I understand that in inner-scope we have access to outer-scope variables but not the inverse, but I still do not understand the technique that is applied here, I hope you can help me to understand:

var nums = [1,2,3];

for (var i = 0; i < nums.length; i++) {
var num = nums[i];
elem.addEventListener('click', (function(numCopy) {
    return function() {
        alert(numCopy)
    };
})(num));

}
    
asked by anonymous 05.11.2015 / 22:01

1 answer

3

I will try to explain what happens in the code. The first point worth noting is the "Declaration Hoisting": link Which means raising the variable declarations to the top of the scope, which can be global or function. That is:

for(var i=0; i < nums.length; i++)

It's how you do it:

var i;
for(i = 0; i < nums.length; i++)

In this way the variable i will be available for use by everyone within the scope.

And this is why when you add a click this way:

elem.addEventListener('click', function() {
        alert(nums[i])
});

It will always result in the last number being displayed. Because the anonymous function you created has access to variables i and nums that were in the same scope as the anonymous function. Meaning that at the moment the function is executed the variable i contains the index of the last Array element.

IIFE

To mitigate this problem, we use an Immediately Invoked Function Expression (IIFE), a function that executes immediately when it is created. Usually expressed this way:

(function(){

}())

Or

(function(){

})()

Although I personally prefer the first option. By doing what you demonstrate in the code, the following happens:

  • IFFE runs during the loop and receives as a parameter the value contained in the current Array index;

    elem.addEventListener('click', (function(num) {
    
    })(nums[i]));
    
  • As a new role has been declared, a new scope is created. This scope has a variable called num that contains the value contained in the index of the array that the function was called with.

  • You return a new function which, when executed, will call the alert function, passing it as a parameter;

    return function() {
      alert(num);
    };
    
  • This new function, which was created in the return clause, is then added as an event listener;

    elem.addEventListener('click', (function(num) {
       return function() {
         alert(num)
       };
    })(nums[i]));
    
  • In your case, clicking always displays the last Array number because, as I understand it, you call addEventListener multiple times in the same element. So, when adding a new listener, the old one is overwritten.

        
    06.11.2015 / 00:48