Wednesday 1 February 2017

Javascript closures - variables vs parameters



I'm trying to learn Javascript closures. I'm having trouble getting my head around the fact that when you create several closures in a loop, all closures save only the last state of a variable. With this example



var links = document.getElementsByTagName('a');

for (var x=0; x

function attachListener() {
links[x].addEventListener('click', function(){
console.log(x);
}, false);
};


When I have three links in my document, clicking on any link shows "3", I guess because x got incremented to 3 after the final run of the loop. I read in this excellent intro that if you run the outer function multiple times a new closure's created each time. So how come each closure doesn't save a different value for x each time I call the outer function?



When you pass x as a parameter to the outer function it does work as expected.




var links = document.getElementsByTagName('a');

for (x=0; x
function attachListener(z) {
links[z].addEventListener('click', function(){
console.log(z);
}, false);
};



Now you get 0 when you click the first link, 1 on the second etc.



Can anyone please explain why there is this difference?



Cheers


Answer



I've been somewhat irritated by this very same behavior in the past. It seems like a bug in the implementation of closures, to me. A closure is supposed to include a snapshot of "the function's lexical environment (e.g., the set of available variables and their values) at the time when the closure was created" (source: Wikipedia; emphasis mine). Manifestly that is not quite what is happening in this example.




But it's easy enough to reason out what is happening behind the scenes. In your first example, there is only a single instance of the variable x, and when the closure is created the JavaScript runtime is storing in it a reference to x instead of a copy of the current value of x at the time the closure is created. So when the loop increments x, the "copy" in the closure appears to increment as well.



In the second case, you pass x as a parameter to the function, which copies the current value of x into a new variable when it gets passed to the attachListener() function. The value of this copy is never updated (i.e. it is decoupled from x, and you do not modify it inside of attachListener()), and thus the closure works as expected, because it stores a reference to the copy and not to the original x.


No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...