Can someone explain to me (clearly and concisely) why this code works the way it does? I come from a strongly typed background in Java (6 and 7) where closures don't exist and do not function the way they do in javascript. I think the concepts related to this question are: closures and scope chain.
Here's the example:
var myfuncs = function() {
var funcs = []
var i;
for (i = 0; i < 10; i++) {
funcs[i] = function() { console.log(i); }
}
return funcs;
}
var allfuncs = myfuncs();
allfuncs.forEach(function(fn) { fn(); });
The above example logs 9 (10 times), but the expectation and my own intuition was thinking it would log 0-9.
Why does this work the way it does in Javascript? Closures are very powerful, but I'm trying to grasp the concept once and for good! A slightly modified example produces the right output, but why?
var myfuncs = function() {
var funcs = []
var i;
for (i = 0; i < 10; i++) {
funcs[i] = (function(index) { console.log(index); })(i);
}
return funcs;
}
var allfuncs = myfuncs();
allfuncs.forEach(function(fn) { fn(); });
Closures aren't unique to Javascript, but I want to see why they are powerful in the context of when javascript is actaully written to interface with the browser/dom.
Does anyone have good, practical examples of how we can apply the closure technique when interfacing with the browser/dom?
Thanks.
In the examples you have, it is very simple.
In your first example, there is only one variable i and everything references that single value. So.. it prints the number 9 ten times. Each function captured a shared value of i that changes.
In the second example you are using a closure. Each function has a private variable called index which receives -- and here is the important part -- a copy of the value i.
So, you get 0 through 9 because there are ten functions, each one with a private index variable and each of those index variables get a snapshot of i as it existed at the time.
This, longer form of a closure, may help:
function myFactory(index) {
return function() {
console.log(index);
}
}
var myfuncs = function() {
var funcs = []
var i;
for (i = 0; i < 10; i++) {
funcs[i] = myFactory(i);
}
return funcs;
}
var allfuncs = myfuncs();
allfuncs.forEach(function(fn) { fn(); });
Related
I don't have much JavaScript experience. My question is this:
When I'm writing a JavaScript library, and many of the functions I'm writing functions are meant to call each other and users can call the functions I'm defining on each other in ways I might not have predicted but are valid, how do I keep the iterators in functions that have iterating loops straight?
Do I have to come up with new names for each iterator in a for loop every time I do a for-loop just to be safe that I haven't accidentally used the same variable in two functions where one function might nest inside the other in a situation I haven't predicted or thought of?
These are just a couple examples of functions that have iteration in them. Everything I'm writing is for working with interacting with Qualtrics surveys (shown in gif examples below).
function watchSet(set, mathFunction) {
var setSize = set.length;
for (var i=0; i < setSize; i++) {
set[i].down().observe("keyup", mathFunction );
}
}
function mathSum(set, output) {
var setTotal = 0;
for (var j=0; j < (set.length); j++) {
var setInputValue = parseInt(set[j].down().value, 10);
if (isNaN(setInputValue)) { setInputValue = 0; }
setTotal = setTotal + setInputValue;
}
output.value = setTotal;
}
function validateError(array, color) {
if (color === undefined) {
color = "pink";
}
color = color.concat(";");
for (var k=0; k < array.length; k++) {
array[k].down().setAttribute("style", "background-color: ".concat(color));
}
$('NextButton') && $('NextButton').hide();
}
function cellRange(startCell, endCell) {
var r1 = /^[A-Z]/;
var r2 = /[0-9]{1,3}$/;
var startCellColumn = r1.exec(startCell)[0].charCodeAt(0) - 61;
var endCellColumn = r1.exec(endCell)[0].charCodeAt(0) - 61;
var startCellRow = parseInt(r2.exec(startCell)[0], 10);
var endCellRow = parseInt(r2.exec(endCell)[0], 10);
var tempRange = [];
for (var q=startCellColumn; q<=endCellColumn; q++) {
for (var r=startCellRow; r<=endCellRow; r++) {
tempRange.push(q);
tempRange.push(r);
}
}
var outputRange = [];
for (var s=0; s < tempRange.length; s+=2) {
outputRange.push(cell(String.fromCharCode(tempRange[s]+61).concat(tempRange[s+1])));
}
return outputRange;
}
Gif Examples:
setting equivalency-validation
summing a couple cells
No, you don't need unique variable names in different functions.
Variables declared with var are local to the function scope in which they are declared in. They will not and do not conflict with anything outside that scope. So, your three functions watchSet(), mathSum() and validateError() can all use var i just fine and will not conflict with each other or with any third party code outside of those functions. Local variables like this are created uniquely each time the function is run and can be referred to only from within that function.
If you did not use var to explicitly declare your loop variables, then Javascript would "implicitly" create global variables by that name and then, yes, your different functions could collide if one function doing this called another so thus they were both trying to use the same global at the same time. But, as long as your variables are declared with var and your code is in a function (thus not running at the global scope), this will not happen.
You can also run your code in strict mode (highly recommended) because then an accidential implicit global is an immediate error and the interpreter will immediately show you where the problem is.
Or use .forEach()
You can also use .forEach() on arrays and not have to create your own iteration index at all.
function watchSet(set, mathFunction) {
set.forEach(function(item) {
item.down().observe("keyup", mathFunction );
});
}
Or, use let in an ES6 environment
In an ES6 environment, you can use let instead of var and the variable will be scoped to only the for loop too.
function watchSet(set, mathFunction) {
var setSize = set.length;
// when declaring with let in a for loop, the variable is scoped to
// only inside the for loop
for (let i=0; i < setSize; i++) {
set[i].down().observe("keyup", mathFunction );
}
// with let in a for declaration, even another use in the same function
// does not conflict
// this is a completely different variable than the one above
for (let i=0; i < setSize; i++) {
set[i].up().observe("keyup", mathFunction );
}
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Javascript closure inside loops - simple practical example
I was trying to code something similar to this :
var funcs = [];
for (var i=0; i<5 ; ++i) {
funcs[i]=function() {
alert(i);
};
}
Obviously, calling funcs[0] won't alert 0 as expected, simply because the variable i is captured by the anonymous function, and calling any of funcs[0..4] will alert '4' (the value of i after the last iteration and shared by all created functions).
The first work around that comes to my mind is using some kind of function generator :
var funcs = [];
for (var i=0; i<5 ; ++i) {
funcs[i]=(function(cap) {
return function() {alert(cap)};
})(i);
}
This do the trick, but seems really puzzling and hard to read. Is there any better way to get the intended behavior without using a function wrapper?
The .bind function allows you to pre-bind additional parameters to a bound function:
var funcs = [];
for (var i=0; i<5 ; ++i) {
funcs[i]=function(i) {
alert(i);
}.bind(this, i);
}
This is an ES5 function, so should work on IE9+, Chrome, Safari, Firefox:
You should write it as simple as you can. I think it's easy to understand, but hard to read. So one way to simplify it is to use Nested coding style. I don't think it can't be simpler than as it is now.
I'd suggest this way:
var funcs = [];
for (var i = 0; i < 5; ++i) {
funcs[i] = (
function (cap) {
return function () { alert(cap) };
}
)(i);
}
IMHO, named functions are often superior, for both performance and readability reasons. Why not do it like this:
function foo (cap) {
return function () { alert(cap) };
}
var funcs = [];
for (var i=0; i<5 ; ++i) {
funcs[i]=foo(i);
}
Try this:
var funcs = [0, 1, 2, 3, 4].map(function(i) {
return function() {alert(i);};
});
Note: map isn't suppoted by IE8 and older, but there's a common polyfill for it.
When you don't want to embed your code with those anonymous functions, a solution is to define (named) class embedding both the state (i) and the function (in the prototype). That's more LOC but sometimes more readable :
var funcs = [];
function MyFunc(i) {
this.i=i;
}
MyFunc.prototype.doIt = function(){
alert(this.i);
};
for (var i=0; i<5 ; ++i) {
funcs[i]=new MyFunc(i);
}
funcs[2].doIt();
My code:
for (var i = 0; i < mapInfos.length; i++) {
var x = function () { doStuff(i); };
google.maps.event.addListenerOnce(mapInfos[i].map, 'tilesloaded', x);
}
The doStuff method simply alerts the value of i. mapInfos has two entries, so you'd expect it to alert 0 and 1, but instead it alerts 2 and 2. I can appreciate vaguely why it is doing this (although var i should keep it local to the scope of the loop?) but how can I make it work as intended?
edit — note that when first posted, the original question included a link to a jsfiddle that seemed to be a relevant example of what the current question is trying to achieve, only it appears to work ...
The code in the jsfiddle works because there's only one "i" in that code. The "i" used in the second loop (where the functions are actually called) is the same "i" as used in the first loop. Thus, you get the right answer because that second loop is running "i" through all the values from zero through four again. If you added:
i = 100;
functions[0]();
you'd get 100 printed out.
The only way to introduce a new scope in JavaScript is a function. One approach is to write a separate "function maker" function:
function makeCallback(param) {
return function() {
doStuff(param);
};
}
Then in your loop:
for (var i = 0; i < mapInfos.length; i++) {
var x = makeCallback(i);
google.maps.event.addListenerOnce(mapInfos[i].map, 'titlesloaded', x);
}
That'll work because the call to the "makeCallback" function isolates a copy of the value of "i" into a new, unique instance of "param" in the closure returned.
Create a new scope for it.
Functions create scope.
function doStuffFactory(i) {
return function () { doStuff(i); };
}
for (var i = 0; i < mapInfos.length; i++) {
var x = doStuffFactory(i);
google.maps.event.addListenerOnce(mapInfos[i].map, 'tilesloaded', x);
}
Change it to
var x = function (param) { doStuff(param); };
Obviously what is going on is that you are alerting a variable that is changing. With the above change it copies it so even if i changes it will still alert the right value.
Javascript doesn't have block scope, so you don't get an x that's local to the loop. Yea!
It has function scope, though.
Yep, weird isn't it!Pointy has an explanation
I have no idea why your first example worked (I wasn't expecting it to) Pointy has an explanation of why your first example worked - The reason why your second one doesn't is because i is scoped to the function containing the for loop, not to the scope defined by the for loop. In fact the only things that have scope in JavaScript are functions. This means that by the time your function gets executed i is 2.
What you need to do is create a scope, for example:
for (var i = 0; i < mapInfos.length; i++) {
var x = (function() {
return function () { doStuff(i); };
})(i);
google.maps.event.addListenerOnce(mapInfos[i].map, 'tilesloaded', x);
}
See JavaScript closures in for-loops for more.
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Javascript closure inside loops - simple practical example
Rather than explaining the question, I'll give an example:
for (var i = 0; i < 100; i ++) {
get_node(i).onclick = function() {
do_something_very_important(i);
}
}
Is there any way to have the value of i substituted into the function upon creation rather than execution? Thanks.
Yes, you can, but that won't work for the example you provided. You would be having a very common closure problem in that for loop.
Variables enclosed in a closure share the same single environment, so by the time the onclick callback is called, the for loop will have run its course, and the i variable will be left pointing to the last value it was assigned. In your example, the do_something_very_important() function will be passed the value 100 for each node, which is not what you intend.
You can solve this problem with even more closures, using a function factory:
function makeClickHandler(i) {
return function() {
do_something_very_important(i);
};
}
// ...
for(var i = 0; i < 100; i++) {
get_node(i).onclick = makeClickHandler(i);
}
This can be quite a tricky topic, if you are not familiar with how closures work. You may want to check out the following Mozilla article for a brief introduction:
Mozilla Dev Center: Working with Closures
UPDATE:
You could also inline the above function factory as #adamse suggested in the other answer. This is actually a more common approach, but is practically the same as the above:
for(var i = 0; i < 100; i++) {
get_node(i).onclick = (function(p) {
return function () {
// we could have used i as a parameter variable as well,
// but we're using p to better illustrate what's happening
do_something_very_important(p);
}
})(i);
}
Any yet another solution is to enclose each iteration in its own scope, by using self invoking anonymous functions:
for(var i = 0; i < 100; i++) {
(function (p) {
// we now have a separate closure environment for each
// iteration of the loop
get_node(i).onclick = function() {
do_something_very_important(p);
}
})(i);
}
Yes this works...
for (var i = 0; i < 100; i++) {
get_node(i).onclick = (function(i) {
return function () {
do_something_very_important(i);
}
})(i);
}
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
I am very puzzled about this code:
var closures = [];
function create() {
for (var i = 0; i < 5; i++) {
closures[i] = function() {
alert("i = " + i);
};
}
}
function run() {
for (var i = 0; i < 5; i++) {
closures[i]();
}
}
create();
run();
From my understanding it should print 0,1,2,3,4 (isn't this the concept of closures?).
Instead it prints 5,5,5,5,5.
I tried Rhino and Firefox.
Could someone explain this behavior to me?
Fixed Jon's answer by adding an additional anonymous function:
function create() {
for (var i = 0; i < 5; i++) {
closures[i] = (function(tmp) {
return function() {
alert("i = " + tmp);
};
})(i);
}
}
The explanation is that JavaScript's scopes are function-level, not block-level, and creating a closure just means that the enclosing scope gets added to the lexical environment of the enclosed function.
After the loop terminates, the function-level variable i has the value 5, and that's what the inner function 'sees'.
As a side note: you should beware of unnecessary function object creation, espacially in loops; it's inefficient, and if DOM objects are involved, it's easy to create circular references and therefore introduce memory leaks in Internet Explorer.
I think this might be what you want:
var closures = [];
function createClosure(i) {
closures[i] = function() {
alert("i = " + i);
};
}
function create() {
for (var i = 0; i < 5; i++) {
createClosure(i);
}
}
The solution is to have a self-executing lambda wrapping your array push. You also pass i as an argument to that lambda. The value of i inside the self-executing lambda will shadow the value of the original i and everything will work as intended:
function create() {
for (var i = 0; i < 5; i++) (function(i) {
closures[i] = function() {
alert("i = " + i);
};
})(i);
}
Another solution would be to create yet another closure which captures the correct value of i and assigns it to another variable which would "get caught" in the final lambda:
function create() {
for (var i = 0; i < 5; i++) (function() {
var x = i;
closures.push(function() {
alert("i = " + x);
});
})();
}
Yes closures are working here. Each time you loop the function you are creating grabs the i. Each function you create shares the same i. The problem you are seeing is that since they all share the same i they also share the final value of i since it is the same captured variable.
Edit: This article by Mr. Skeet explains closures in some depth and addresses this issue in particular in a way that is much more informative then I have here. However be careful as the way that Javascript and C# handle closures have some subtle differences. Skip to the section called "Comparing capture strategies: complexity vs power" for his explanation on this issue.
John Resig's Learning Advanced JavaScript explains this and more. It's an interactive presentation that explains a lot about JavaScript, and the examples are fun to read and execute.
It has a chapter about closures, and this example looks a lot like yours.
Here's the broken example:
var count = 0;
for ( var i = 0; i < 4; i++ ) {
setTimeout(function(){
assert( i == count++, "Check the value of i." );
}, i * 200);
}
And the fix:
var count = 0;
for ( var i = 0; i < 4; i++ ) (function(i){
setTimeout(function(){
assert( i == count++, "Check the value of i." );
}, i * 200);
})(i);
Just defining an inner function, or assigning it to some variable:
closures[i] = function() {...
does not create a private copy of the whole execution context. The context isn't copied until the nearest outer function is exiting (at which point those external variables could be garbage collected, so we'd better grab a copy).
This is why wrapping another function around your inner function works - the middle guy actually executes and exits, cuing the innermost function to save his own copy of the stack.
Here is what you should do to achieve your result:
<script>
var closures = [];
function create() {
for (var i = 0; i < 5; i++) {
closures[i] = function(number) {
alert("i = " + number);
};
}
}
function run() {
for (var i = 0; i < 5; i++) {
closures[i](i);
}
}
create();
run();
</script>