This is probably an easy question but it's late at night and I can't get my head round this.
here's my code
$(document).ready(function () {
var items = getNumber();
for (var i = 0; i < items.length; i++) {
var test = items[i].action;
test();
}
});
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({ id: i, number: num, action: function () { alert(i) } });
}
return items
}
Could someone explain to me why the alert output is always 5? I want the alert parameter to be the index at the time it is added to the array. It seems like it is being dynamic.
If you could also post a solution how i could get this to work i would be extremely thankful
This is a common issue with JavaScript variable scoping: new variables are only introduced in a new execution context and thus, in the problematic code, i is shared across all the action callbacks.
Anyway, here is the corrected code following the standard idiom:
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
// _i is a new variable for each callback
action: (function (_i) {
// use separate (one per callback) _i variable
return function () { alert(_i) }
})(i) // pass in current value for loop
});
}
return items
}
Alternatively, if one doesn't like all the nesting, it's fine to use a "named" function to perform the same task. The key to point is that the closure is created (and returned from) a new execution context so that a different variable is closed-over:
function getNumber()
{
function mkAction (i) {
return function () { alert(i) }
}
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
action: mkAction(i)
});
}
return items
}
Another approach is to use Function.bind from ES5:
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
action: (function (_i) { alert(_i) }).bind(null, i)
});
}
return items
}
(Note that Function.bind can be emulated using a new execution context/closure even without native browser support. See the MDC documentation.)
See also:
JavaScript closure inside loops – simple practical example
Passing functions to setTimeout in a loop: always the last value?
How do JavaScript closures work?
Related
I have a for loop
for (var i = 0; i < markers.length; i++) {
markers[i]["index"] = i;
}
After click to marker , Always return last item
marker.bindPopup(popup).on('popupopen',function(e){
console.log(marker["index"]);
//return last item
})
Here is an example of what is the underlying problem. It is a problem of "closures". It is a complicated topic that a lot of JS developers don't understand.
function withLet() {
for (let i = 0; i < 3; i++) {
const expectedI = i;
setTimeout(() => {
console.log('withLet', { expectedI, actualI: i });
});
}
}
function withVar() {
for (var i = 0; i < 3; i++) {
const expectedI = i;
setTimeout(() => {
console.log('withVar', { expectedI, actualI: i });
});
}
}
withLet();
withVar();
About why...
The var keywork creates the variable in the scope of the function withVar. If any function inside of the withVar function needs to access the i variable it will get the reference shared between all of the iterations of the loop.
All functions, be it event handlers or simple setTimeout will thus get the reference of the variable after all synchronous code has run.
The let keyword created a new closure, one closure per loop iteration, thus if any function inside the for loop needs to access i, it will get the reference of its own and only i.
for (let i = 0; i < 3; i++) {
const expectedI = i;
setTimeout(() => {
console.log('withLet', { expectedI, actualI: i });
});
}
console.log(i); // Error! Variable "i" is not defined!
Wondering if there is by any chance to programmatically setting third statement of forloop
var conditionProgrammatically = 'i++';//or 'x--'
for (var i = 0; i < 10; conditionProgrammatically) {
console.log(i)
}
You can use any expression you want there including calling a function. You just need to be careful of scope. So, for example, this works:
var conditionProgramatically = () => i++ ;
for (var i = 0; i < 10; conditionProgramatically()) {
console.log(i)
}
But it depends on the fact that var i is in a scope shared by the function. This, however, doesn't work:
var conditionProgramatically = () => i++ ;
for (let i = 0; i < 10; conditionProgramatically()) {
console.log(i)
}
Because let is scoped to the block and not available.
Of course you can share an object which is mutable by passing it as an argument like:
fn = (o) => o.i += 1
for (let o = {i:0}; o.i < 10; fn(o)) {
console.log(o.i)
}
This allows you to use let, but is a little hard on the eyes.
All said, it's probably going to be easier to make your logic fit in a simple expression rather than calling a function. You can still perform some logic, though:
for (let i = 0; Math.abs(i) < 10; i = Math.random() > .65 ? i -1: i + 1) {
console.log(i)
}
You can set a variable and then operate with this variable according to your needs.
(remember that i-- is equivalent to i -= 1).
BTW, be careful because you would also have to change the condition, if not you will end up in an infinite loop. In your case, I would use abs()
var step = 1; // or var step = -1;
for (var i = 0; abs(i) < 10; i += step) {
console.log(i)
}
Usually, in functional programmings (like python and javascript), we can use dictionary (or objects) to store functions.
var myFunctions = {
"a": function (i) { return i + 1 },
"b": function (i) { return i - 3 }
};
Then, we can set the condition as the key to the dictionary:
myCondition = "a"; // this will set condition to increment by 1
Here is your for loop:
for (i = 0; i < n; i = myFunctions[myCondition](i)) {
// whatever
}
I need to be able to build an array of anonymous functions that execute setTimeout with unique arguments per function. The following code has already been proposed, and works, on SO, but it only deals with immediate execution of the setTimeout. It does not explain how one can accumulate a collection of setTimeout functions to an array, with each setTimeout function having its own closure that defines a unique variable to be used in the execution of the setTimeout.
This is what exists already on SO:
var strings = [ "hello", "world" ];
for(var i=0;i<strings.length;i++) {
setTimeout(
(function(s) {
return function() {
alert(s);
}
})(strings[i]), 1000);
}
This is one of many permutations of what I have tried:
var strings = [ "hello", "world" ];
var func_array = [];
for (var i=0; i < strings.length; i++) {
func_array.push(function() {
setTimeout(
(function(s) {
return function() {
alert(s);
}
})(strings[i]), 1000);
});
}
When apply() is called on func_array, the strings[i] value is the same for each call, i.e. the final member of the strings array.
Another attempt: here we make the function that gets pushed onto func_array self-executing in order to scope the strings[i] variable correctly. However, apply() sees only 'undefined' since the function has already executed.
var strings = [ "hello", "world" ];
var func_array = [];
for (var i=0; i < strings.length; i++) {
func_array.push(function(s) {
setTimeout(
(function(s) {
return function() {
alert(s);
}
})(s), 1000);
}(strings[i]));
}
Ideas?
Use Function.prototype.bind(). It becomes much simpler than it actually is.
var strings = [ "hello", "world" ];
var func_array = [];
for (var i=0; i < strings.length; i++) {
var fn = function(string){
return setTimeout(function(){ alert(string) } ,1000);
};
func_array.push(fn.bind(this, strings[i]));
}
This does what you want.
var strings = ["hello", "world"];
var func_array = [];
for (var i = 0; i < strings.length; i++) {
(function (s) {
var func = function () {
alert("String is " + s);
}
var futureFunc = function () {
setTimeout(func, 1000);
}
func_array.push(futureFunc);
} (strings[i]));
}
func_array[0]()
func_array[1]()
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.
So my basic setup is this:
for (var i = 0; i < 3; i++) {
var indices = [-1, -1, -1];
while (index == -1) {
// Do Stuff
index[i] = newIndex;
}
var press = function() { alert(i); };
new control({press: press});
}
Now when I press the each of the new controls instead of getting alert(0), alert(1) and alert(2) I get alert(3), alert(3) and alert(3).
I can kind of understand whats going on. Now my question: how can i pass the different indexes to the function as I intended?
It is because closure variable i, the solution is to create a private closure for each loop.
for (var i = 0; i < 3; i++) {
var indices = [-1, -1, -1];
while (index == -1) {
// Do Stuff
index[i] = newIndex;
}
var press = (function(myvar){
return function() { alert(myvar); };
})(i);
new control({press: press});
}
Use closure:
var press = (function (x) {
return function () {
alert(x);
};
})(i);
This way the current i value is saved in a safe place, a private function.
Note that declaring variables (with var) inside loops are not standard, you should declare the press variable outside the loop.