Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does one JavaScript closure work and the other doesn't?

There are two versions, supposedly when the user click the first link, it will alert "1", and the second link, "2", etc.:

Version 1:

<a href="#" id="link1">click me</a>
<a href="#" id="link2">click me</a>
<a href="#" id="link3">click me</a>
<a href="#" id="link4">click me</a>
<a href="#" id="link5">click me</a>

<script type="text/javascript">
    for (i = 1; i <= 5; i++) {
        document.getElementById('link' + i).onclick = (function() {
            return function() {
                var n = i;
                alert(n);
                return false;
            }
        })();
    }
</script>

Version 2:

<a href="#" id="link1">click me</a>
<a href="#" id="link2">click me</a>
<a href="#" id="link3">click me</a>
<a href="#" id="link4">click me</a>
<a href="#" id="link5">click me</a>

<script type="text/javascript">
    for (i = 1; i <= 5; i++) {
        document.getElementById('link' + i).onclick = (function() {
            var n = i;
            return function() {
                alert(n);
                return false;
            }
        })();
    }
</script>

Version 1 will not work. Version 2 will. I think I know the reason why, but would like to compare with other people's explanations as to why version 1 doesn't work.

like image 892
nonopolarity Avatar asked Jul 27 '10 12:07

nonopolarity


2 Answers

Version 1 does not work because there's a common variable "i" (a global variable in this case, because you forgot var) that is shared by every "click" handler function the loop creates.

In the second version, you create a new lexical scope with the little wrapper function. That gives each "click" handler it's very own private "i".

like image 120
Pointy Avatar answered Oct 05 '22 23:10

Pointy


In the second example you create a var n = i; it makes i value scoped inside the onclick function. While at the first one the onclick function still uses global value of i

I'd suggest this usage instead:

  for (i = 1; i <= 5; i++) {
    document.getElementById('link' + i).onclick = (function(i) {
      return function() {
        alert(i); 
        return false;
      }
    })(i);
  }

In this case you'll get the same behaviour since i will be the local variable for onclick function as it's an argument.

like image 23
fantactuka Avatar answered Oct 06 '22 00:10

fantactuka