Hi I want to create several function which work similar but use a slightly different value. I cannot change the parameters because I use these for a callback of a library. My first idea:
for(i = 0; i < 3; i++) {
f = function() {
console.log(i);
};
}
This obviously copies a refence of i to the function but I rather want to value at that time so that each function outputs a different value. I appreciate your help.
you may try this way:
var f=[];
for(i = 0; i < 3; i++) {
f[i] = (function(index) {
return function() {
console.log("My value: " + index);
}
})(i);
}
working demo here
Related
I have a code sample
for(var i = 0; i < 3; i++) {
setTimeout() {
console.log("i " + i)
}
}
This is printing "3" to the console three times. However, I want it to print "0, 1, 2" without using let.
So far, I have tried the following -
var funcs = [];
function createfunc(i) {
return function() {
console.log("i ", i)
}
}
for(var i = 0; i < 3; i++) {
setTimeout(() => {
funcs[i] = createfunc(i) // statement 1
}, 1000)
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
This is working fine if I use the statement 1 just as it is without putting it into the setTimeout function. However, it is throwing an error if I'm using the setTimeout function. Can someone let me know how can I make print 0, 1, 2 to the console using the setTimeout function ?
It happens because of closure, anyway you can use the below codes, then you will be getting the proper result that you want.
for(var i = 0; i < 3; i++) {
((i) => {
setTimeout(() => {
console.log("i " + i)
}, 0);
})(i);
}
In continuation to this question, I tried the following code that uses the same variable in both the loops and I get the desired result. My question is, WHY ?
So the initial code is:
var funcs = [];
for (var i = 0; i < 3; i++) { // let's create 3 functions
funcs[i] = function() { // and store them in funcs
console.log("My value: " + i); // each should log its value.
};
}
for (var j = 0; j < 3; j++) {
funcs[j](); // this will not give desired output
}
The outputs this:
My value: 3
My value: 3
My value: 3
Whereas, the intended output is:
My value: 0
My value: 1
My value: 2
Now, if I use variable 'i' (a global variable) in the second loop as well, the code looks like:
var funcs = [];
for (var i = 0; i < 3; i++) { // let's create 3 functions
funcs[i] = function() { // and store them in funcs
console.log("My value: " + i); // each should log its value.
};
}
for ( i = 0; i < 3; i++) {
funcs[i](); // this gives desired output
}
I get the intended output,
My value: 0
My value: 1
My value: 2
WHY?
Now, if I use variable 'i' (a global variable) in the second loop as
well ... I get the intended output ... WHY?
Because when you do the first executing loop:
for (var j = 0; j < 3; j++) {
funcs[j]();
}
the loop constructing the array has finished and the global variable i has the value 3 and therefore console.log(i) results in logging 3 to the console for each iteration of the first executing loop. When you do the second executing loop:
for ( i = 0; i < 3; i++) {
funcs[i]();
}
you for each iteration of the loop assign a new value to the global variable i and it is this new value that the console.log(i) will log to the console: 0, 1, 2.
How do you achieve the desired result in a simple way?
You can use let to achieve the desired result in a simple way:
"use strict"; // use of let requires strict mode
var funcs = [];
for (let i = 0; i < 3; i++) { // just replace var with let here
funcs[i] = function() {
console.log("My value: " + i);
};
}
for (var j = 0; j < 3; j++) {
funcs[j]();
}
Now the array will be constructed with functions having access to a local variable, which is assigned the value of i at the time of adding the function to the array.
Because the variable i is contaminated. In your snippets, The loops write to the same variable i and the functions reads it. Note that there is just one variable i in your code.
What you really need to get your intended result is called "module pattern", which wraps a function to keep a local copy of the variable in the loop:
for (var i=0; i<3; i++) {
(function (j) {
funcs[j] = function() { // and store them in funcs
console.log("My value: " + j); // each should log its value.
};
})(i);
}
You can read this article to get more information about "module pattern".
I have the following code:
for(var i = 0; i <=3; i++){
setTimeout(function(){
var j = i;
function(){
console.log(j);
}
}, 1000);
}
I am getting an error 'Function name expected' on the second function. I don't understand why I can't use an anonymous function here.
To create a closure and call it right away do this:
(function(){
console.log(j);
}());
But, to actually capture the i correctly for the inner function, you can move the function up a bit and pass it i:
for (var i = 0; i <= 3; i++) {
(function(i){
setTimeout(function(){
console.log(i);
}, 1000);}(i));
}
And, to actually print the numbers 1 second after each other, you can do this:
for (var i = 0; i <= 3; i++) {
(function(i){
setTimeout(function(){
console.log(i);
}, 1000*(i+1));}(i));
}
Based on Jordão's answer, I ended up using the following code:
for(var i = 0; i <=3; i++){
(function(){
var j = i;
setTimeout(function(){
console.log(j);
}, 1000);
}());
}
So you know for the future you can get your output with a smaller (and cleaner?) number of lines of code if you use recursion:
function loop(i) {
console.log(i = i || 0);
if (i <= 3) setTimeout(loop, 1000, ++i);
}
loop();
DEMO
This question already has answers here:
How do JavaScript closures work?
(86 answers)
Closed 7 years ago.
I have code that looks like this:
var a = [];
for(var i = 0; i < 10; i++) {
a[i] = function() {
console.log(i);
}
}
Unfortunately, it seems that i is being passed by reference, so all the functions in a output 10. How do I make it so that each function outputs the value that i had when it was created? I.e. a[0]() gives 0, a[1]() gives 1, etc.
EDIT: to clarify, I do not want a to store the values 0-9. I want a to store functions that return the values 0-9.
You need to invoke a function (to create a closure that captures your value) which returns a function (the one you want to end up with). Something like this:
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = (function(value) {
return function() {
console.log(value);
}
})(i);
}
var a = [];
for(var i = 0; i < 10; i++) {
a[i] = (function(j) {
return function () {
console.log(j);
}
})(i);
}
A better performing version -
function makeFunction(i) {
return function () {
console.log(i);
}
}
var a = [];
for(var i = 0; i < 10; i++) {
a[i] = makeFunction(i);
}
JSFiddle Demo.
This is a really basic question but...
I have some code like this
var arr = Array('blah.jpg','ha.jpg');
for (var i=0; i<array.length; i++)
{
$('div#blah' + i).click(function() {
$('img').attr('src', arr[i]); });
}
This should bind the div with id="blah0" to change all images to 'blah.jpg' when clicked.
Similarly, clicking the div with id ="blah1" should change all images to 'ha.jpg'.
However, the anonymous function won't work because it will use the value of 'i' at the time of execution, i.e. 2. This means that clicking either div will try and set all images to arr[2] - a non-existent element (interestingly not throwing a JS error on my machine but that's another story...).
How can I get the anonymous function to be created using the value of 'i' at declaration time?
As a simpler example:
for (var i=0; i<10; i++)
{
$('div#blah'+i).click(function() {
alert(i)); });
}
This should display '0' when I click 'blah0', '1' when I click 'blah1' etc.
However, by default it will display '10' no matter which 'blah' i click.
Declare a new variable inside a function that creates a new click handler that gets the current value of i as a parameter:
function makeClickHandler(arr, local_i) {
return function() {
$('img').attr('src', arr[local_i]);
};
}
var arr = Array('blah.jpg','ha.jpg');
for (var i=0; i<array.length; i++)
{
$('div#blah' + i).click(makeClickHandler(arr, i));
}
Each instance of the function gets its own copy of local_i, that doesn't change each time.
In this particular case you should be using a closure:
for (var i=0; i<10; i++)
{
(function(j){
$('div#blah'+j).click(function() { alert(j)); });
})(i);
}
(function(){ /* code */ })() denotes a self executing function, which implies that it will use and evaluate the value of i immediately in the loop as opposed to at click time.
Have 1 more variable inside the loop and increment it after using it in the closure.
var j = 0;
for (var i=0; i<array.length; i++)
{
$('div#blah' + j).click(function() {
$('img').attr('src', arr[i]); });
j++;
}
I have this answer so far but bit of a hack:
var arr = Array('blah.jpg','ha.jpg');
for (var i=0; i<array.length; i++)
{
eval("$('div#blah' + i).click(function() { $('img').attr('src', arr[" + i + "]); })");
}
Also:
for (var i=0; i<10; i++)
{
eval("$('div#blah'+i).click(function() { alert(" + i + ")); });");
}