Note that there are some explanatory texts on larger screens.

plurals
  1. POReadibility of anonymous closures in nested loops
    text
    copied!<p>A friend of mine got bitten by the all too famous 'anonymous functions in loop' javascript issues. (It's been explained to death on SO, and I'm actually expecting someone to my question as a duplicate, which would probably be fair game). </p> <p>The issue amounts to what John Resig explained in this tutorial : </p> <p><a href="http://ejohn.org/apps/learn/#62" rel="nofollow">http://ejohn.org/apps/learn/#62</a></p> <pre><code>var count = 0; for ( var i = 0; i &lt; 4; i++ ) { setTimeout(function(){ assert( i == count++, "Check the value of i." ); }, i * 200); } </code></pre> <p>To a new user it should work, but indeed "i always have the same values", they say, hence crying and teeth gnashing.</p> <p>I explained the issue with a lot of hand waving and some stuff about scopes, and pointed him to some solutions provided on SO or other sites (really, when you know it is such a common issue, google <em>is</em> your friend). </p> <p>Of course the real answer is that in JS the scope is at the function level. So when the anonymous functions run, 'i' is not defined in the scope of any of them, but it is defined in the global scope, and it has the value of the end of the loop, 4. </p> <p>Since we've all most likely be trained in languages that use block-level scope, it's yet-another-thing-that-js-does-a-little-bit-different-than-the-rest-of-the-world (meaning of "this", anyone ?)</p> <p>What bogs me is that the common answer, actually even the one provided by John himself is the following : </p> <pre><code>var count = 0; for ( var i = 0; i &lt; 4; i++ ) (function(i){ setTimeout(function(){ assert( i == count++, "Check the value of i." ); }, i * 200); })(i); </code></pre> <p>Which obviously works and demonstrate mastery of the language and a taste for nested parenthesis that suspiciously makes you look like a LISP-er.</p> <p>And yet I can't help thinking that other solutions would be <em>much</em> more readible and easier to explain. </p> <p>This one is just pushing the anonymous closure a bit <strike>closure</strike> closer to the setTimeout (or closer to the addEventListener is 99.9% of the case where it bites someone) : </p> <pre><code>var count = 0; for (var i = 0 ; i &lt; 4 ; i++) { setTimeout((function (index) { return function () { assert( index == count++, "Check the value of i." ); } })(i), i*200); }; </code></pre> <p>This one is about explaining what we're doing, with an explicit function factory : </p> <pre><code>var count = 0 function makeHandler(index) { return function() { assert(index == count ++); }; }; for (var i = 0 ; i &lt; 4 ; i++) { setTimeout(makeHandler(i), i*200); }; </code></pre> <p>Finally, there is another solution that removes the problem altogether, and would seem even more natural to me (although I agree that it somehow sidesteps the problem 'by accident')</p> <pre><code>var count = 0; function prepareTimeout(index) { setTimeout(function () { assert(index == count++); }, index * 200); }; for (var k = 0; k &lt; 4 ; k++) { prepareTimeout(k); }; </code></pre> <p>Are those solutions entirely equivalent, in terms of memory usage, number of scope created, possible leaking ?</p> <p>Sorry if this is really a FAQ or subjective or whatever. </p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload