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
Post a Comment