0

If the arguments to functions are passed by value in javascript how come the console.log() below seems to get its input by reference? Shouldn't each time it is invoked inside setTimeout just the value of i be passed and not its reference?

let i;

for (i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i);
  }, i * 1000);
}
Andy
  • 61,948
  • 13
  • 68
  • 95
  • i will print 6 in every iteration, because of the delay in execution – ControlAltDel Mar 09 '22 at 18:55
  • If you add a new line before the `setTimeout`: `console.log(i);` can you see what's going on? – Andy Mar 09 '22 at 18:56
  • You declare you variable i. Then you assign a value to it, i = 1, inside the for loop syntax. Then the loop runs 6 times instantly, and on each loop creates the setTimeout call with timeout of i * 1000, so the first time console.log is invoked after 1 second, the second console.log after 2 seconds etc. But since the loop itself has already ran, the value of i is 6. – qristjan Mar 09 '22 at 18:57
  • 1
    Pass by value or pass by refence doesn't apply at all here. You're not passing anything to the enclosed `timer` function. You're just referencing `i` from the lexical environment. Look into closures. – Ivar Mar 09 '22 at 19:00

4 Answers4

1

for (let i = 1; i <= 5; i++) {
  setTimeout(function timer() {
    console.log(i);
  }, i * 1000);
}
1

Shouldn't each time it is invoked inside setTimeout just the value of i be passed and not its reference?

Yes, and this is exactly what is happening. It is passing the value of i by value to console.log(i). But, you're passing that value AFTER the entire for loop has completed when i has its terminal value because this is the order that things run:

  1. You declare let i.
  2. You start your for loop.
  3. The for loop runs to completion, each iteration starting an additional timer. The timer is non-blocking and asynchronous so it just starts the timer and execution of the loop continues.
  4. Then, sometime later (after the for loop is completely done) with the value of i sitting on its terminal value, then the timers start firing which causes them to call their callback and then output console.log(i).

Note, that if you made the let i be part of the for loop declaration, you would get completely different results:

for (let i = 1; i <= 5; i++) {
  // Because let i was inside the for loop declaration,
  // there is a separate copy of i for each iteration of the for loop here
  setTimeout(function timer() {
    console.log(i);
  }, i * 1000);
}

Because here there is special treatment for the let i in the for loop. Each iteration of the loop gets it own separate copy of i and thus it still has the value it had when the loop iteration was run at the later time when the timer fires.

This is completely different than your original version where there is only one i for the entire loop and its value is incremented on each iteration and thus the value i had when each iteration of the loop ran is no longer available when the timer fires.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
1

for (let i=1; i<=5; i++) {
    setTimeout( function timer(){
        console.log( i );
    }, i*1000 );
}

This has to do with a very important concept called scope. A scope is the piece of code where your variable exists.

In your example you define the variable before the for loop. So this happens internally:

  • You create i
  • i is set to 1
  • i is referred to in your setTimeout() (5 times)
  • i is incremented by 1 ( 5 times )
  • i is logged 5 times, all as 6, because the values all refer to the same i

In my example i is scoped in the for loop. This means every iteration of the loop a new reference to i is generated. So these references refer to different variables from the computers point of view ( even tough your code names them all i ).

Rob Monhemius
  • 4,822
  • 2
  • 17
  • 49
0

setTimeOut is an asynchronous function. It means it won't stop execution of other synchronous code. Each Console Log will wait 1 second to execute. But as the value of i has become 6 it will print that 5 times.

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Meyssam Toluie Mar 09 '22 at 20:46