JavaScript closure inside loops – simple practical example -


var funcs = [];  (var = 0; < 3; i++) {      // let's create 3 functions    funcs[i] = function() {          // , store them in funcs      console.log("my value: " + i); // each should log value.    };  }  (var j = 0; j < 3; j++) {    funcs[j]();                      // , let's run each 1 see  }

it outputs this:

my value: 3
value: 3
value: 3

whereas i'd output:

my value: 0
value: 1
value: 2


the same problem occurs when delay in running function caused using event listeners:

var buttons = document.getelementsbytagname("button");  (var = 0; < buttons.length; i++) {          // let's create 3 functions    buttons[i].addeventlistener("click", function() { // event listeners      console.log("my value: " + i);                  // each should log value.    });  }
<button>0</button><br>  <button>1</button><br>  <button>2</button>

what's solution basic problem?

well, problem variable i, within each of anonymous functions, bound same variable outside of function.

what want bind variable within each function separate, unchanging value outside of function:

var funcs = [];    function createfunc(i) {      return function() { console.log("my value: " + i); };  }    (var = 0; < 3; i++) {      funcs[i] = createfunc(i);  }    (var j = 0; j < 3; j++) {      funcs[j]();                        // , let's run each 1 see  }

since there no block scope in javascript - function scope - wrapping function creation in new function, ensure value of "i" remains intended.


update: relatively widespread availability of array.prototype.foreach function (in 2015), it's worth noting in situations involving iteration on array of values, .foreach() provides clean, natural way distinct closure every iteration. is, assuming you've got sort of array containing values (dom references, objects, whatever), , problem arises of setting callbacks specific each element, can this:

var somearray = [ /* whatever */ ]; // ... somearray.foreach(function(arrayelement) {   // ... code code code 1 element   someasynchronousfunction(arrayelement, function() {     arrayelement.dosomething();   }); }); 

the idea each invocation of callback function used .foreach loop own closure. parameter passed in handler array element specific particular step of iteration. if it's used in asynchronous callback, won't collide of other callbacks established @ other steps of iteration.

if happen working in jquery, $.each() function gives similar capability.

update 2: ecmascript 6 (es6), newest version of javascript, starting implemented in many evergreen browsers , backend systems. there transpilers babel convert es6 es5 allow usage of new features on older systems.

es6 introduces new let , const keywords scoped differently var-based variables. example, in loop let-based index, each iteration through loop have new value of i each value scoped inside loop, code work expect. there many resources, i'd recommend 2ality's block-scoping post great source of information.

for (let = 0; < 3; i++) {     funcs[i] = function() {         console.log("my value: " + i);     }; } 

beware, though, ie9-ie11 , edge prior edge 14 support let above wrong (they don't create new i each time, functions above log 3 if used var). edge 14 gets right.


Comments

Popular posts from this blog

java - UnknownEntityTypeException: Unable to locate persister (Hibernate 5.0) -

python - ValueError: empty vocabulary; perhaps the documents only contain stop words -

ubuntu - collect2: fatal error: ld terminated with signal 9 [Killed] -