Question

I am confused in this topic which is important to me in terms of asynchronous function calls and closures. I must have missed the most important part of javascript function invocation and wasn't able to find an answer to my question up to now, hence I hope for your help!

My question assumes:

  1. An outer function fDynamic is called with some argument i ( = 0 )
  2. fDynamic queues an inner function fInnerQueued, to be invoked at some point in time in the future.
  3. In the mean time the outer function fDynamic is invoked again with different argument i ( = 1 )

Is this new argument value now visible to the first inner function fInnerQueued which will see the changes as soon as it is finally invoked? ... it seems not?

It seems as if the function arguments are not free variables and are bound tightly to the function invocation scope even trough asynchronous calls.

Here's a fiddle and here the essence:

var j = 0;
var fDynamic = function( i ) {
  j = i; // j seems to be a free variable, while i is not?
  if( i == 0 ) {
    // Delay execution in order to let i change to 1
    var fInnerQueued = function() {
      console.log(i + ' ('+ j + ')'); //should be "1 (1)" but is "0 (1)"
    }
    setTimeout( fInnerQueued, 100);
  } else {
    console.log(i); // is "1 (1)", right!
  }
}; // Expected output "1 (1)", "1 (1)" but it is "1 (1)", "0 (1)"...
fDynamic(0);
fDynamic(1);

I tested it also on node.js and got the same, somewhat unexpected, output.

I crawled through different posts and didn't manage to find an answer:

I know I can omit the hassle by creating shielded closures or using bind. But I really want to understand why function arguments are not considered free variables to inner functions and they are even retained through asynchronous calls.

Was it helpful?

Solution

Function arguments are locally scoped variables.

function foo (x) {

}
foo(1);

is roughly the same as

function foo () {
    var x = 1;
}
foo();

not

function foo () {
    x = 1;
}
foo();

The argument is closed over when you use an inner function.

Calling the function again creates a new variable in a new scope.

OTHER TIPS

It's quite simple: i is a function context variable, when the parser finished parsing the function it's deleted. j is a variable in the global context and therefore it isn't deleted when the function exits.

When you call the function twice, a new context is created for each call and i is in this since i isn't a class variable but a parameter. The first time you call it, the 'old' context is preserved so that your child function can execute in a child context, in which i is still 0. When this happens the i of the second call is already gone, only j in the global context is preserved.

There is no difference whether you calling function synchronous or async way (e.g. via setTimeout) as Quentin correctly point out

Function arguments are locally scoped variables.

Please see the updated fiddle (fStatic is called 2 times for parity with fDynamic)

appendLog("Static Argument, changed within function (expected: 0, 1, 0, 1):");
var fStatic = function( i ) {
var fPrintArg = function() {
 appendLog( i );
};
setTimeout(fPrintArg, 100);
fPrintArg();
}; 
fStatic(0);
fStatic(1);

the results are the same: i var is scoped for each outer function call.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top