0

When I created 5 buttons in a loop and click the value of i is always 6

function createButtons() {
   for (var i = 1; i <= 5; i++) {
     var body = document.getElementsByTagName("BODY")[0];
     var button = document.createElement("BUTTON");
     button.innerHTML = 'Button ' + i;
     (function(num) {
       button.onclick = function() {
          alert('This is button ' + num);
       }
     })(i);
     body.appendChild(button);
   }
}

but when I change the scope of i to block scope(using IIFE or let keyword) it gives the right value of i. How does it working under the hood of javascript?

Shubham
  • 189
  • 9
  • it works exactly as you have described (the code in the question is the latter variant, am I correct?) – Jaromanda X May 25 '18 at 00:16
  • 2
    Pretty simple: without the closure the value of `i` ***at the time event occurs*** is at it's maximum – charlietfl May 25 '18 at 00:16
  • I get why the problem happened I am just not able to understand how creating a block scope for "i" solves it. does javascript create different copies of "i" on each iteration when in block scope? – Shubham May 25 '18 at 00:22
  • `i` is passed to the IIFE, which takes it as the parameter `num`, and parameters are scoped to their functions, so `num` is scoped to the IIFE. – Sebastian Simon May 25 '18 at 00:24
  • so basically each IIFE is a different object ? @Xufox – Shubham May 25 '18 at 00:28
  • @Shubham In this case, yes, since a new function is created each iteration of the loop. But this isn’t necessary. You could also assign the function from the IIFE to an outer variable and still call it with `i` as an argument inside the loop. This way, there’s only one function, but the behavior will be the same, because `num` is scoped to the function, not to the loop. – Sebastian Simon May 25 '18 at 00:47
  • [Related](https://stackoverflow.com/a/37793317/1541563). [Related](https://stackoverflow.com/a/30900289/1541563). – Patrick Roberts May 25 '18 at 03:17

1 Answers1

0

I have seperated your functions into incorrect and correct one.

The incorrect version is what you're asking. The correct version is what you've already figured out but don't know why it work.

In the incorrect version, the value of i will always be updated to the most recent value because i belongs to the createButtons function and is shared with all onclick handler, and it is changing with the loop.

In the correct version, the value of i is given to the IIFE as num, and num belongs to the IIFE and not to createButtons.

Because of that, num is fixed because a new num is created for every loop thus is not shared with the other onclick handler.

Why? It is how closure works in JavaScript.

Read this for deeper understanding on JavaScript closure.

function createButtons_incorrect() {
   for (var i = 1; i <= 5; i++) {
     var body = document.getElementsByTagName("BODY")[0];
     var button = document.createElement("BUTTON");
     button.innerHTML = 'Bad ' + i;
     button.onclick = function() {
        alert('This is button ' + i);
     }
     body.appendChild(button);
   }
}

function createButtons_correct() {
   for (var i = 1; i <= 5; i++) {
     var body = document.getElementsByTagName("BODY")[0];
     var button = document.createElement("BUTTON");
     button.innerHTML = 'Good ' + i;
     (function(num){
       button.onclick = function() {
          alert('This is button ' + num);
       }
     })(i);
     body.appendChild(button);
   }
}

createButtons_incorrect();
document.body.appendChild(document.createElement('br'));
createButtons_correct();
yqlim
  • 6,898
  • 3
  • 19
  • 43