educative.io

Unexpected behavior of variables in closures

Question:i don’t understand why the output is 9, 9, 9 instead of 0, 1, 4 after reading the explanation.
After reading the quote: ’ The anonymous function is evaluated when it is invoked and using the execution context it resolves the value of i , using the context’s scope chain. When you call giveMeFunctions() , the i variable is set to 3 at the time it returns.’ question 1: i don’t understand what is context’s scope chain. question 2: I don’t understand why variable is set to 3 at the time it returns

Issues with closure variables Listing 8-9: View the console output

Hey @chao_ren!

Regarding your question, let us first discuss what exactly is a scope? The scope is basically a defined area where the execution context has access to all the declared variables, functions, parameters inside of it. An execution context is an environment where the code gets executed.

Now, it is important to remember that except for the global scope, each scope is connected with one another. Hence, the scope chain is basically a list containing all the specific connections that each scope has with one another.

So what is happening over here is that the variable i , within the giveMeFunctions function, is already bounded to the same variable outside of the function, which you can observe by looking at the following coding snippet:

for (var i = 0; i < myFunctions.length; i++) {
      console.log(i + ": " + myFunctions[i]());
    }

This, therefore, sets the value of i to 3, since it is much above the list in the scope chain than the giveMeFunctions and so when the giveMeFunctions is invoked, the value of i is already 3. Hence, why the output of 9, 9, 9 appears.

Hope this helps!

Hey @Ramez_Salman !

I’m still having hard time understanding this. If you got time, could you please do a break down or step by step? Helps me understand better. How is i set to 3?

Hey @Siem!

So, the most important part of this code is the following snippet present in the for loop of the giveMeFunctions:

functions[i] = function () {
  return i * i;
}

This is an asynchronous function called a callback. So what happens over here is that there is a concept called an event loop in Javascript. (For more details and a better understanding of what this is, please check the following link). In simplest terms, this is a model that allows Javascript to achieve concurrency.

In the code given in the lesson, the giveMeFunctions function runs and places this particular callback function (the one shown above) on to the callback queue which is whether all the processes to be executed are placed.

Now, after doing this, it then resumes its other work which is iterating through the for loop. An important thing to note here is that over here, the callback function isn’t called but rather, the for loop is simply performing iteration without calling this callback function. However, what happens during the iteration is that the callback function is continuously placed in the callback queue each time the iteration takes place.

Once it reaches the end of the iteration (when i becomes 3), the event loop comes back around and checks to see if there are any callbacks in the callback queue that are to be executed. It will then find the callbacks that were placed in the for loop iteration and execute them all using the current value of i which is 3.

Basically something like the following:

# The for loop starts and `i` has the value of 0 at the start of the loop.
i = 0
# The callback gets placed in the callback queue.
queue = [callback_i0]
functions[0] = [callback_i0 function]
# The for loop moves to the next iteration step. 
# `i` now becomes 1. 
i = 1
# The callback again gets placed in the callback queue.
queue = [callback_i0, callback_i1]
functions[1] = [callback_i1 function]
# Once again, the for loop moves to the next iteration step, and `i` becomes 2. 
i = 2
# The callback again gets placed in the callback queue.
queue = [callback_i0, callback_i1, callback_i2]
functions[2] = [callback_i2 function]
# Again, the for loop moves to the next iteration step, and `i` becomes 3.
i = 3
# Now, over here, since we have reached the end of the for loop, 
# the event loop comes back to check if there are any callbacks present in the callback queue. 
# Since there are, it will execute these using the current value of i (this is 3 now). 

functions[0] = [callback_i0 function]
functions[0] = [i * i]
functions[0] = [3 * 3]
functions[0] = [9]

# The above will work in the same manner for the other callback functions placed in the callback queue. 

Hence, why the output of 9, 9, 9 appears. One good way of checking this is basically putting console.log in the asynchronous function and checking how the callback works. Like the following:

function giveMeFunctions() {
  var functions = [];
  for (var i = 0; i < 3; i++) {
    console.log("BEFORE CALLBACK");
    functions[i] = function () {
      console.log("CALLBACK");
      return i * i;
    }
    console.log("AFTER CALLBACK");
  }
  return functions;
}

When you run this, you see that the BEFORE CALLBACK console runs first followed by the AFTER CALLBACK console, and then once all of these are completed, it is only then the CALLBACK console is executed.

Hope this helps!

1 Like

Wow, very impressive. Thank you!