Why is this javascript for loop running only once? - javascript

function f1() {
for (i = 0; i <= 5; i++)
console.log(i);
}
function foo() {
for (i = 0; i < 5; i++)
f1();
}
foo();
Hi, I'm trying to understand why the result of executing foo is:
0
1
2
3
4
5
And not:
0
1
2
3
4
5
0
1
2
3
4
5
0
1
2
3
4
5
0
1
2
3
4
5
0
1
2
3
4
5
It's a slide I am reading about JS and its talking about when you don't use var then it is defined on the global object and provides this example without any further details why we get the result.
I thought it will simply loop and run the f1 function each time until its less than 5.
Please help me understand.
Thanks

The problem is in your iterators (i):
for (i = 0; i <= 5; i++)
i is global, and both your for loops test against it, making them only run once, and aborting when i == 5.
So, what happens is this:
When you call foo(), foo tells the js interpreter to create a variable in the global scope called i, and set it to 0. Then foo calls f1.
There, the for loop sets i, which already exists, to 0, and runs it's loop like it should, incrementing i up to 5.
Then, it's time for the second iteration of the loop in foo, so it checks if i < 5, but it's not (i==6 (5 from f1, +1 from foo)), so it will not call f1 again.
To fix this, either declare them in the function's local scope using var:
function f1() {
for (var i = 0; i <= 5; i++)
console.log(i);
}
function foo() {
for (var i = 0; i < 5; i++)
f1();
}
foo();
Or, use different variables:
function f1() {
for (i = 0; i <= 5; i++)
console.log(i);
}
function foo() {
for (j = 0; j < 5; j++)
f1();
}
foo();
However, this second option is a bad idea, since it will both place i and j in the global scope, which is asking for conflicts. I'd suggest using the var option.

If you write your loop like
for (i = 0; i < 5; i++)
here i refers to a global variable, because you have not declared it with the var keyword, and since you use i in both functions, they end up actually using the same variable.
You should then replace both loops with
function f1() {
for (var i = 0; i <= 5; i++)
console.log(i);
}
function foo() {
for (var i = 0; i < 5; i++)
f1();
}
foo();
As your code is originally written, i is a shared global variable, so foo starts, sets is to 0 then calls f1 which loop on it up to 6, when control returns to foo it founds that i is 6, so i < 5 is false and so it ends the loop.

Related

i++ loops without any problem, i+2 loops infinitely and crashes

I want to create a simple loop function that adds 2 every time it loops. However, even though I tell my for loop to stop if the number reaches or is less than 100, it goes past 100 and loops infinitely.
i++ works just fine:
function addTwo() {
for (i = 0; i <= 100; i++) {
console.log(i);
}
}
addTwo();
When I change it to i+2 it crashes:
function addTwo() {
for (i = 0; i <= 100; i + 2) {
console.log(i);
}
}
addTwo();
I expect the console to log:
0
2
4
6
8
...
100.
But instead it loops infinitely and crashes.
i+2 in your case does nothing. JS evaluates it and then does nothing with the calculated value, this means that i is never increased.
++ is a special operator that increments the variable preceding it by 1.
To make the loop work you have to assign the value of the calculation i+2 to the variable i.
for (i=0; i<=100; i = i+2) {
console.log(i);
}
or
for (i=0; i<=100; i += 2) {
console.log(i);
}
i++ increments i. But, i+2 doesn't update the value of i. You should change it to i += 2
function addTwo() {
for (i = 0; i <= 100; i += 2) {
console.log(i);
}
}
addTwo();
The third parameter of a for is the final-expression:
An expression to be evaluated at the end of each loop iteration. This occurs before the next evaluation of condition. Generally used to update or increment the counter variable.
In your case you are not assigning any value to i. You should replace it with something like this:
function addTwo() {
for (i=0; i<=100; i+=2) {
console.log(i);
}
}
addTwo();
i++ is a short hand for i += 1 which is called Increment Operator But i+2 or even i+1 will not increase the value of i. You need to increase it by assigning a new value to i. i = i + 2 or i += 2.
Number is one of the primitive types in javascript which mean you can't change it unless you use assignment operator =
Note: You are not using let or var with i this will make i a global variable.
function addTwo() {
for (let i = 0; i <= 100; i+=2) {
console.log(i);
}
}
addTwo();
for (i = 0; i <= 20; i++) {
console.log(i);
i++;
}
You can increase i twice Or
for (i=0; i<=100; i+=2) {
console.log(i);
}
you can use i+=2 it will increase value of i 2 times and set new value of i.

What's the scope of local vars in javascript for statement headers?

function test()
{
i = 10;
for (var i = 0; i < 1; i++);
alert(i);
}
I've tested this in Firefox. Does it give the same result in all browsers? Is the i in the for statement header local to the for statement or to the function?
var is function scoped, therefore your function
function test()
{
i = 10;
for (var i = 0; i < 1; i++);
alert(i);
}
will declare an i variable, override it with another one with similar name in a for, resulting in 1 and this value will be alerted.

javascript for-loop and timeout function

I am facing an issue with a for-loop running a setTimeout.
for (var x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
I expect the output
0
1
3
4
Yet, for some reason they all output 5.
Variable x is defined in the local scope of the for-loop, so I thought this may not count for the callback of setTimeout. I tested with defining x outside of the for-loop.
var x = 10
for (var x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
I figured this output would be giving 10, yet it didn't. Then I thought it would make sense to define x afterwards.
for (var x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
var x = 10
This does return only 10. This implies the callbacks are all called after the for-loop is executed? And why do they only conform to the parent scope of the for-loop once the variable is initialised after execution of the for-loop? Am I missing something?
I know how make this example working with
for (var x = 0; x < 5; x++) {
var timeoutFunction = function(x) {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(x), 1)
}
Yet, I really wonder what is missing...
Note that specifying 1 as the delay value will not actually cause the function to execute after 1 millisecond. The fastest the function could execute is roughly 9 milliseconds (and that's based on the internals of the browser), but in reality, it can't run the function until there is no other code running.
As for the unexpected output, you are experiencing this behavior because the timeoutFunction includes a reference to the x variable that is declared in a higher scope. This causes a closure to be created around the x variable, such that each time the function runs it does not get a copy of x for itself, but it is sharing the x value because it is declared in a higher scope. This is the classic side effect of closures.
There are a few ways to adjust the syntax to fix the issue...
Make a copy of x and let each function use its own copy by passing x into the function. When you pass a primitive type (boolean, number, string) a copy of the data is created and that's what is passed. This breaks the dependence on the shared x scope. Your last example does this:
for (var x = 0; x < 5; x++) {
var timeoutFunction = function(x) {
return function() {
console.log(x)
}
}
// Because you are passing x into the function, a copy of x will be made for the
// function that is returned, that copy will be independent of x and a copy will
// be made upon each loop iteration.
setTimeout(timeoutFunction(x), 1)
}
Another example that does the same thing would be needed if your timeout function wasn't returning another function (because there would be no function to pass the value to). So, this example creates an extra function:
for (var x = 0; x < 5; x++) {
// This time there is no nested function that will be returned,
function timeoutFunction(i) {
console.log(i);
}
// If we create an "Immediately Invoked Funtion Expression" (IIFE),
// we can have it pass a copy of x into the function it invokes, thus
// creating a copy that will be in a different scope than x.
(function(i){
setTimeout(function(){
timeoutFunction(i); // i is now a copy of x
}, 1);
}(x));
}
If you are working with browsers that support the ECMAScript 2015 standard, you can simply change the var x declaration in your loop to let x, so that x gets block level scope upon each iteration of the loop:
// Declaring a variable with let causes the variable to have "block-level"
// scope. In this case the block is the loop's contents and for each iteration of the
// loop. Upon each iteration, a new "x" will be created, so a different scope from
// the old "x" is what's used.
for (let x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
You have to create new scope for your function to make it 'remember' value from given iteration:
for (var x = 0; x < 5; x++) {
var timeoutFunction = (function(x) {
return function() {
console.log(x)
}
})(x)
setTimeout(timeoutFunction(), 1)
}
Another solution is to use ES2015 let:
for (let x = 0; x < 5; x++) {
var timeoutFunction = function() {
console.log(x)
}
setTimeout(timeoutFunction(), 1)
}
use this snippet
for(let x = 0; x < 5; x++) {
var timeoutFunction = function() {
console.log(x);
};
setTimeout(timeoutFunction, 1);
};
console.log('For loop completed');
JavaScript is not multi threaded so with your code the timeoutFunction will execute after the for loop is completed since you are using the global variable (with respect to timeoutFunction context) the final result is printed

Closure Inside For Loop

I know that one of the ways to log 0 to 9 with this code:
EDIT: Source
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}), 10)
}
jsfiddle
is to make setTimeout self invoking and pass i as a parameter, like so:
for(var i = 0; i < 10; i++) {
setTimeout((function(i) {
console.log(i);
})(i), 10)
}
but I've tested making setTImeout self invoking without passing i, and it still works:
for(var i = 0; i < 10; i++) {
setTimeout((function() {
console.log(i);
})(), 10)
}
jsfiddle
My questions:
Why does it work even without passing i as a parameter?
Is it necessary to pass i?
Problem
It's not a Closure.
for(var i = 0; i < 10; i++) {
setTimeout((function(i) {
console.log(i);
})(i), 10)
}
setTimeout expects actually a function as parameter, but you made it invoking immediately. So it logs immediately the value of i, without waiting. setTimeout has now the return of your anonymous function as first parameter which is undefined.
The same here:
for(var i = 0; i < 10; i++) {
setTimeout((function() {
console.log(i);
})(), 10)
}
It's executed immediately, it doesn't wait for the 10ms. The only difference you didn't passed i as parameter, but it will look in the parent scope for a variable called i – and there is one.
You will see that your anonymous function is called immediately if you set the time to e.g. one second (1000).
Solution
A real closure would look like this:
Without parameter: You will see 10 times 10, because at time of execution of the inner function the loop is already finished which means i equals 10 at that time:
for(var i = 0; i < 10; i++) {
(function() {
setTimeout(function() {
console.log(i);
},10);
})();
}
With parameter – you will get the expected result:
for(var i = 0; i < 10; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
},10);
})(i);
}
In all your avobe code, you are executing the function given as the first parameter in setTimeout without returning anything. So what happens is you are returning undefined.
In the code below, I am also self-invoking, but returning a function for setTimeout to execute after a specific intervali * 10.
This is the closure function. Check the console logs.It prints the time stamp in miliseconds. There is a gap of 10ms.
for (var i = 0; i < 10; i++) {
setTimeout((function(i) {
return function() {
console.log(i, performance.now() + 'ms');
}
})(i), i * 10)
}
Additional Info
In the comment section, I have a query
what's the point, when you could just put the setTimeout inside an anonymous function?
So I guess this is the code you are suggesting.
for (var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i, performance.now() + 'ms');
}, i * 10)
}
If you run this snippet, you will get a log of 10 for every log. This is the beauty of closures. When you are executing the annonymous function
function() {
console.log(i, performance.now() + 'ms');
}
It cant find i in current scope and searches it parent scope. And voila, it finds i, but unfortunately the loop has completed its iteration by that time and its value = 10 now. So logged 10 every time.
This is why we need closure.

javascript closure in loop

Following code is given:
var a = [ ], i = 0, j = 0;
for (i = 0; i < 5; i += 1) {
(function(c) {
a.push(function () {
console.log(c); });
})(i);
};
for (j = 0; j < 5; j += 1) { a[j](); }
Why does i always get bigger by 1 instead of staying 5? Hasn't the foor loop already been passed, so the i parameter given to the anonymous function should be 5?
If you referenced i from the inner closure then yes, you would see the result being 5 in all cases. However, you pass i by value to the outer function, which is accepted as parameter c. The value of c is then fixed to whatever i was at the moment you created the inner closure.
Consider changing the log statement:
console.log("c:" + c + " i:" + i);
You should see c going from 0 to 4 (inclusive) and i being 5 in all cases.
chhowie's answer is absolutely right (and I upvoted it), but I wanted to show you one more thing to help understand it. Your inner function works similarly to a more explicit function call like this:
var a = [ ], i = 0, j = 0;
function pushFunc(array, c) {
array.push(function () {
console.log(c);
});
}
for (i = 0; i < 5; i += 1) {
pushFunc(array, i);
}
for (j = 0; j < 5; j += 1) { a[j](); }
Which should also help you understand how c comes from the function argument, not from the for loop. Your inner function is doing exactly the same thing as this, just without an externally declared named function.

Categories

Resources