It's a matter of scope. Code has access to the variables declared within the function, its containing function (if any), its containing function (if any), and so on, and then globals.
In your first example, readFileCallback
is declared outside the form.on
callback, and so it doesn't have access to things inside the form.on
callback.
In your second example, the function is inside the form.on
callback, and so it does have access to the things inside it.
Note that in the second example, in theory a new function is created each time the callback is called. That's fine, JavaScript engines are really fast at creating functions (and good ones will reuse the code even though a separate function object is created).
Normally you want to create the function at the outermost location where it has access to everything it needs. So in your case, that would be inside form.on
, but outside the readFile
callback. Which is exactly where your second example has it. But you can use a named function like your first example if you like, just put it in form.on
's callback:
form.on('file', function(name, file){
fs.readFile(file.path, readFileCallback);
function readFileCallback(err, contents){
console.log(file);
if (err) throw err;
....
}
});
Let's take an example where everything had a simple name, and follow through two calls:
function outer(outerArg) {
function middle(middleArg) {
function inner(innerArg) {
console.log("innerArg = " + innerArg);
console.log("middleArg = " + middleArg);
console.log("outerArg = " + outerArg);
}
inner(middleArg.toLowerCase());
}
middle(outerArg.toUpperCase());
}
outer
contains middle
which contains inner
, and outer
calls middle
(and middle
calls inner
). A call:
outer("Test1");
outer
gets the arg "Test1"
- It calls
middle
with "TEST1"
- It calls
inner
with "test1"
inner
outputs:
innerArg = test1
middleArg = TEST1
outerArg = Test1
So far, so simple, but it's more exciting than that: What if middle
returns a function that calls inner
, instead of calling it immediately, and outer
returns returns middle
's return value?
function outer(outerArg) {
function middle(middleArg) {
function inner(innerArg) {
console.log("innerArg = " + innerArg);
console.log("middleArg = " + middleArg);
console.log("outerArg = " + outerArg);
}
function caller() { // ***
inner(middleArg.toLowerCase()); // ***
} // ***
return caller; // ***
}
return middle(outerArg.toUpperCase()); // ***
}
Now, calling outer
doesn't have any output at all:
var f = outer("Test2");
But then calling the function middle
returned (caller
) does:
f();
Output:
innerArg = test2
middleArg = TEST2
outerArg = Test2
The arguments still exist after outer
and middle
return! But it's even more interesting:
var f1 = outer("Test3");
var f2 = outer("Test4");
f2(); // Note -- calling the second one first
f1();
Output:
innerArg = test4
middleArg = TEST4
outerArg = Test4
innerArg = test3
middleArg = TEST3
outerArg = Test3
So that means, two outerArg
s still existed after both calls to outer
had finished, along with two middleArgs
. How?
They exist on objects attached to the functions:
- Calling
outer
creates an execution context (an object), which amongst other things (and leaving out a lot of details) holds the arguments and local variables for that call to outer
. Let's call it the "outer context." It also has a reference to the execution context containing it (the global context, in our code). Normally that object gets cleaned up when a functon returns...
- ...but
outer
creates a function, middle
. When you create a function, the current execution context is attached to the function. That's how it has access to the variables and such in that outer context.
outer
calls middle
, creating an inner execution context, and middle
creates two other function (inner
and caller
), which each get that inner context attached to them. middle
then returns caller
, so caller
exists after the call to middle
completes. Since caller
has a reference to the inner execution context, the context continues to exist (just like any other object), even though middle
has returned. Since that context has a reference to inner
, inner
also continues to exist.
outer
returns the return value of middle
(which is caller
), and so that means caller
still exists when outer
returns, which means the inner context it refers to still exists, which means inner
still exists, and the outer context still exists because the inner context has a reference to it.
...which is how f1
and f2
have access to those arguments after outer
returns: When you run them, they look up the values in the contexts attached to them.