javascripthoistingfunction-expressionjavascript-function-declaration

Why doesn't the location of a function expression matter in node js?


In JavaScript, when using a function expression (e.g. var myFunc = function() {...}), like any other variable declaration, you have to define the variable before using it. For example, the following example will not work (will result in Uncaught TypeError: myFunc is not a function):

var myVar = myFunc();

var myFunc = function() {
    // code here...
}

However, in my routes/index.js file of a node js project, I have the following code (abbreviated obviously):

var router = express.Router();
.
.
.
router.post('/', function(req, res) {
    ...
    ...
    var myVar = myFunc(); // WORKS!
    ...
    ...
}

var myFunc = function() {
    ...
}

The myFunc variable is declared after it is used, so shouldn't this throw an error?


Solution

  • There are two aspects to this:

    1. Why can the callback passed to router.post access the myFunc variable, when the variable declaration is below it in the code?

    2. Why can the callback passed to router.post access the function assigned to the myFunc variable, when it's not assigned to the variable until below it in the code?

    First we'll deal with the variable, then we'll deal with its value (the function we assign to it):

    The myFunc variable is declared after it is used, so shouldn't this throw an error?

    It would if var weren't hoisted. But var is hoisted. So when your module is loaded, first the JavaScript engine looks through it to find all variable declarations and function declarations, processes those, and then does the step-by-step code in your module.

    E.g., to the JavaScript engine, your module actually looks like this:

    var router = undefined;
    var myFunc = undefined;
    
    router = express.Router();
    .
    .
    .
    router.post('/', function(req, res) {
        var myVar = undefined;
        ...
        ...
        myVar = myFunc(); // WORKS!
        ...
        ...
    }
    
    myFunc = function() {
        ...
    }
    

    More on this on my anemic little blog: Poor misunderstood var

    Why doesn't the location of a function expression matter in node js?

    It does. The only reason you're not running into trouble is that myFunc isn't used until the callback you're passing into router.post is called, which is after the top-level scope code of your module script has been run (including the expression creating myFunc).

    E.g., what happens is:

    1. The prep stuff (creating the vars router and myFunc and setting them to undefined)

    2. The express.Router call, which assigns a new value to router

    3. The call to router.post, passing in but not calling a callback function

    4. Creation of the function, which assigns a new value to myFunc

    5. Later, when a request comes in, the callback is called, by which time myFunc has been created (and is accessible because the callback is a closure over the context where myFunc is declared)

    This is analogous to this:

    setTimeout(function() {
        var myVar = myFunc();
    }, 0);
    
    var myFunc = function() {
        // code here...
    }
    

    First setTimeout is called, then the function is created and assigned to myFunc, then later the callback function is called and calls the function via myFunc.