{"id":803,"date":"2014-06-05T07:00:00","date_gmt":"2014-06-05T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2014\/06\/05\/closing-over-the-loop-variable-is-just-as-harmful-in-javascript-as-it-is-in-c-and-more-cumbersome-to-fix\/"},"modified":"2014-06-05T07:00:00","modified_gmt":"2014-06-05T07:00:00","slug":"closing-over-the-loop-variable-is-just-as-harmful-in-javascript-as-it-is-in-c-and-more-cumbersome-to-fix","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20140605-00\/?p=803","title":{"rendered":"Closing over the loop variable is just as harmful in JavaScript as it is in C#, and more cumbersome to fix"},"content":{"rendered":"<p>\nPrerequisite reading:\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/ericlippert\/archive\/2009\/11\/12\/closing-over-the-loop-variable-considered-harmful.aspx\">\nClosing over the loop variable considered harmful<\/a>.\n<\/p>\n<p>\nJavaScript has the same problem.\nConsider:\n<\/p>\n<pre>\n<i>function hookupevents() {\n for (var i = 0; i &lt; 4; i++) {\n  document.getElementById(\"myButton\" + i)\n   .addEventListener(\"click\",\n         function() { alert(i); });\n }\n}<\/i>\n<\/pre>\n<p>\nThe most common case where you encounter this is when you are hooking\nup event handlers in a loop, so that&#8217;s the case I used as an example.\n<\/p>\n<p>\nNo matter which button you click, they all alert <code>4<\/code>,\nrather than the respective button number.\n<\/p>\n<p>The reason for this is given in the prerequisite reading:\nYou closed over the loop variable,\nso by the time the function actually executed,\nthe variable <code>i<\/code> had the value <code>4<\/code>,\nsince that&#8217;s the leftover value after the loop is complete.\n<\/p>\n<p>\nThe cumbersome part is fixing the problem.\nIn C#, you can just copy the value to a scoped local\nand capture the local,\nbut that doesn&#8217;t work in JavaScript:\n<\/p>\n<pre>\n<i>function hookupevents() {\n for (var i = 0; i &lt; 4; i++) {\n  <font COLOR=\"blue\">var j = i;<\/font>\n  document.getElementById(\"myButton\" + i)\n   .addEventListener(\"click\",\n         function() { alert(<font COLOR=\"blue\">j<\/font>); });\n }\n}<\/i>\n<\/pre>\n<p>\nNow the buttons all alert <code>3<\/code> instead of <code>4<\/code>.\nThe reason is that JavaScript variables have\n<i>function<\/i> scope, not block scope.\nEven though you declared <code>var j<\/code> inside a block,\nthe variable&#8217;s scope is still the entire function.\nIn other words, it&#8217;s as if you had written\n<\/p>\n<pre>\n<i>function hookupevents() {\n <font COLOR=\"blue\">var j;<\/font>\n for (var i = 0; i &lt; 4; i++) {\n  <font COLOR=\"blue\">j = i;<\/font>\n  document.getElementById(\"myButton\" + i)\n   .addEventListener(\"click\",\n         function() { alert(j); });\n }\n}<\/i>\n<\/pre>\n<p>\nHere&#8217;s a function which emphasizes this\n&#8220;variable declaration hoisting&#8221; behavior:\n<\/p>\n<pre>\nfunction strange() {\n k = 42;\n for (i = 0; i &lt; 4; i++) {\n  var k;\n  alert(k);\n }\n}\n<\/pre>\n<p>\nThe function alerts <code>42<\/code> four times\nbecause the variable <code>k<\/code> refers to the same\nvariable <code>k<\/code> throughout the entire function,\n<i>even before it has been declared<\/i>.\n<\/p>\n<p>\nThat&#8217;s right.\nJavaScript lets you use a variable before declaring it.\n<\/p>\n<p>\nThe scope of JavaScript variables is the function,\nso if you want to create a variable in a new scope,\nyou have to put it in a new function,\nsince functions define scope.\n<\/p>\n<pre>\nfunction hookupevents() {\n for (var i = 0; i &lt; 4; i++) {\n  <font COLOR=\"blue\">var handlerCreator = function(index) {\n   var localIndex = index;\n   return function() { alert(localIndex); };\n  };\n  var handler = handlerCreator(i);<\/font>\n  document.getElementById(\"myButton\" + i)\n   .addEventListener(\"click\", <font COLOR=\"blue\">handler<\/font>);\n }\n}\n<\/pre>\n<p>\nOkay, now things get weird.\nWe need to put the variable into its own function,\nso we do that by declaring a helper function\n<code>handler&shy;Creator<\/code> which creates event handlers.\nSince we now have a function, we can create a new local variable\nwhich is distinct from the variables in the parent function.\nWe&#8217;ll call that local variable <code>local&shy;Index<\/code>.\nThe handler creator function saves its parameter in the\n<code>local&shy;Index<\/code> and then creates and returns the actual handler\nfunction, which uses <code>local&shy;Index<\/code> rather than <code>i<\/code>\nso that it uses the captured value rather than the original variable.\n<\/p>\n<p>\nNow that each handler gets a separate copy of <code>local&shy;Index<\/code>,\nyou can see that each one alerts the expected value.\n<\/p>\n<p>\nNow, I wrote out the above code the long way for expository purposes.\nIn real life, it&#8217;s shrunk down quite a bit.\n<\/p>\n<p>\nFor example, the <code>index<\/code> parameter itself can be used\ninstead of the <code>local&shy;Index<\/code> variable,\nsince parameters can be viewed as merely conveniently-initialized\nlocal variables.\n<\/p>\n<pre>\nfunction hookupevents() {\n for (var i = 0; i &lt; 4; i++) {\n  var handlerCreator = function(index) {\n   return function() { alert(<font COLOR=\"blue\">index<\/font>); };\n  };\n  var handler = handlerCreator(i);\n  document.getElementById(\"myButton\" + i)\n   .addEventListener(\"click\", handler);\n }\n}\n<\/pre>\n<p>\nAnd then the <code>handler&shy;Creator<\/code> variable\ncan be inlined:\n<\/p>\n<pre>\nfunction hookupevents() {\n for (var i = 0; i &lt; 4; i++) {\n  var handler = <font COLOR=\"blue\">(function(index) {\n   return function() { alert(index); })<\/font>(i);\n  document.getElementById(\"myButton\" + i)\n   .addEventListener(\"click\", handler);\n }\n}\n<\/pre>\n<p>\nAnd then the <code>handler<\/code> itself can be inlined:\n<\/p>\n<pre>\nfunction hookupevents() {\n for (var i = 0; i &lt; 4; i++) {\n  document.getElementById(\"myButton\" + i)\n   .addEventListener(\"click\",\n       <font COLOR=\"blue\">(function(index) {\n         return function() { alert(index); })(i)<\/font>);\n }\n}\n<\/pre>\n<p>\nThe pattern\n<code>(function (x) { ... })(y)<\/code>\nis misleadingly called\nthe <i>self-invoking function<\/i>.\nIt&#8217;s misleading because the function doesn&#8217;t invoke itself;\nthe outer code is invoking the function.\nA better name for it would be the\n<i>immediately-invoked function<\/i>\nsince the function is invoked immediately upon definition.\n<\/p>\n<p>\nThe next step is to change\nthen the name of the helper <code>index<\/code> variable\nto simply <code>i<\/code> so that the connection\nbetween the outer variable and the inner variable can be made\nmore obvious (and more confusing to the uninitiated):\n<\/p>\n<pre>\nfunction hookupevents() {\n for (var i = 0; i &lt; 4; i++) {\n  document.getElementById(\"myButton\" + i)\n   .addEventListener(\"click\",\n       <font COLOR=\"blue\">(function(<font COLOR=\"blue\">i<\/font>) {\n         return function() { alert(<font COLOR=\"blue\">i<\/font>); })(i)<\/font>);\n }\n}\n<\/pre>\n<p>\nThe pattern\n<code>(function (x) { ... })(x)<\/code>\nis an idiom that means\n&#8220;For the enclosed block of code,\ncapture <code>x<\/code> <i>by value<\/i>.&#8221;\nAnd since functions can have more than one parameter,\nyou can extend the pattern to\n<code>(function (x, y, z) { ... })(x, y, z)<\/code>\nto capture multiple variables by value.\n<\/p>\n<p>\nIt is common to move the entire loop body into the pattern,\nsince you usually refer to the loop variable multiple times,\nso you may as well capture it just once and reuse the captured\nvalue.\n<\/p>\n<pre>\nfunction hookupevents() {\n for (var i = 0; i &lt; 4; i++) {\n  <font COLOR=\"blue\">(function(i) {<\/font>\n   document.getElementById(\"myButton\" + i)\n    .addEventListener(\"click\", function() { alert(i); });\n  <font COLOR=\"blue\">})(i);<\/font>\n }\n}\n<\/pre>\n<p>\nMaybe it&#8217;s a good thing that the fix is more cumbersome in JavaScript.\nThe fix for C# is easier to type, but it is also rather subtle.\nThe JavaScript version is quite explicit.\n<\/p>\n<p>\n<b>Exercise<\/b>:\nThe pattern doesn&#8217;t work!\n<\/p>\n<pre>\nvar o = { a: 1, b: 2 };\ndocument.getElementById(\"myButton\")\n .addEventListener(\"click\",\n   (function(o) { alert(o.a); })(o));\no.a = 42;\n<\/pre>\n<p>\nThis code alerts <code>42<\/code> instead of <code>1<\/code>,\neven though I captured <code>o<\/code> by value.\nExplain.\n<\/p>\n<p>\n<b>Bonus reading<\/b>:\nC# and ECMAScript approach solving this problem in two different ways.\nIn C# 5,\n<a HREF=\"http:\/\/www.mindscapehq.com\/blog\/index.php\/2012\/03\/18\/what-else-is-new-in-c-5\/\">\nthe loop variable of a <code>foreach<\/code> loop\nis now considered scoped to the loop<\/a>.\nECMAScript code name Harmony\n<a HREF=\"http:\/\/ariya.ofilabs.com\/2013\/05\/es6-and-block-scope.html\">\nproposes a new <code>let<\/code> keyword<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Prerequisite reading: Closing over the loop variable considered harmful. JavaScript has the same problem. Consider: function hookupevents() { for (var i = 0; i &lt; 4; i++) { document.getElementById(&#8220;myButton&#8221; + i) .addEventListener(&#8220;click&#8221;, function() { alert(i); }); } } The most common case where you encounter this is when you are hooking up event handlers in [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-803","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Prerequisite reading: Closing over the loop variable considered harmful. JavaScript has the same problem. Consider: function hookupevents() { for (var i = 0; i &lt; 4; i++) { document.getElementById(&#8220;myButton&#8221; + i) .addEventListener(&#8220;click&#8221;, function() { alert(i); }); } } The most common case where you encounter this is when you are hooking up event handlers in [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/803","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=803"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/803\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=803"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=803"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=803"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}