This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 4 years ago.
I am new in JavaScript and jQuery. I have found a problem which I can't solve myself.
for (i = 0; i < 12; i++)
{
$("#c" + i).on("click", function () { alert(i) });
}
It attaches events to every element with id from c0 to c11 with alert(12) instead of alert(i)...
On the other hand
$("#c0").on("click", function () { alert(0) });
$("#c1").on("click", function () { alert(1) });
$("#c2").on("click", function () { alert(2) });
...
Works good. Isn't it the same?
This is because of the way var keyword works and also because
$("#c" + i).on("click", function () { alert(i) });`
is async in nature.
just do this instead,
Quick Fix
for (let i = 0; i < 12; i++)
{
$("#c" + i).on("click", function () { alert(i) });
}
Explanation: Your code does not work because $('#c').on('click', fun) is async in nature what that means is this function function () { alert(i) } will be executed later in time, by the time that happens your for loop will be finished with the value of i = 12 hence, you are getting 12 as a value for all handlers.
Now the question is how adding let fixed this?
The answer is simple, Let follows Block scoping whereas var or no var ( global ) is not blocked scoped. var has a functional scope and to achive the same with var you'll need to do something like this.
for(var i = 0; i < 12; i++) {
(function(i) {
$("#c" + i).on("click", function () { alert(i) });
})(i);
}
Related
This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 2 years ago.
I would like to use some codes to listen the click event in some buttons and produce some random strings to a textarea with the use of output_random function, this codes work like a charm:
buttons[0].addEventListener("click", function () { output_random(allTags[0], ranNums[0], outputs[0]) });
buttons[1].addEventListener("click", function () { output_random(allTags[1], ranNums[1], outputs[1]) });
buttons[2].addEventListener("click", function () { output_random(allTags[2], ranNums[2], outputs[2]) });
buttons[3].addEventListener("click", function () { output_random(allTags[3], ranNums[3], outputs[3]) });
but since I am learning Javascript and I thought this kind of codes is not beautiful and would like to make it look like this:
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', function () {
for (var j = 0; j < buttons.length; i++) {
output_random(allTags[j], ranNums[j], outputs[j])
};
});
}
But unfortunately it does not work at all, the output is undefined. Can anyone help me with this? I can't quite understand the for loop in this example.
Thank you
You can try this which should work fine.
function myFunction(i)
{
buttons[i].addEventListener("click", function () { output_random(allTags[i], ranNums[i], outputs[i]) });
}
for (var i = 0; i < buttons.length; i++) {
myFunction(i);
}
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(); });
This question already has answers here:
How do I add a delay in a JavaScript loop?
(32 answers)
Closed 8 years ago.
I have a function set up like this
awesomeFirePenguin: function(){
// function does some stuff, not important
},
and I call it in a for loop like this
for (var i = 0; i < n; i++){
this.awesomeFirePenguin();
}
This works, the function is called n times, but the results of the function are shown immediately after each other. What I want is for the results to be shown after each other with a slight delay, like 100ms or so. Something klike this
for (var i = 0; i < n; i++){
this.awesomeFirePenguin();
// wait a while and then continue
}
I tried using
for (var i = 0; i < n; i++){
window.setTimeout(this.awesomeFirePenguin(), 100);
}
and window.setInterval(this.awesomeFirePenguin(), 100);, but both only execute the function once.
setTimeout and setInterval can take functions as arguments, but what you were doing is calling them. Thus, they were receiving not the function, but its return value as an argument.
If you want to delays to stack, you can just multiple the timeout delay by whatever loop index you're currently at.
for (var i = 0; i < n; i++){
window.setTimeout(this.awesomeFirePenguin.bind(this), 100 * i);
}
The answer by voithos is probably the simplest. You can also use an asynchronous execution library like Clumpy.js, where you could write your code as:
var clumpy = new Clumpy({delay: 100});
var i;
clumpy.for_loop(
function() { i = 0; },
function() { return i < n; },
function() { ++i; },
this.awesomeFirePenguin
);
It's also possible to do this using a closure and a self-invoking function, but it's ugly code that's hard to understand:
(function loop(i, f) {
setTimeout(function () {
f();
if (--i) loop(i, f);
}, 100)
}(n, this.awesomeFirePenguin));
It's possible to write something using setInterval instead of setTimeout, but it's not any easier to comprehend, although it probably eliminates the recursive calls.
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>