سؤال

I'm new to Javascript and functional paradigms. I really like using closure to keep little bits of state wrapped up safely in a private scope. It's a refreshing change from the song and dance of class worship in Java.

I wrote the following code with the intention of printing 0-9 to the console. It works, but I'm surprised that it does.

I don't understand how the next refers to the returned function for the recursive next() call! Is it related to the "late binding" property of Javascript?

var next = (function() {  // begin stateful wrapper function
    var current = -1;

    return function() {   // begin returned function
        current += 1;
        if (current < 10) {
            console.log(current);

            next();       // recursive call 
        } else {
            return;
        }
    };    // end returned function

})();     // immediately call wrapper function

next();   // call returned function to generate output
  • During execution, how does the recursive next() call already refer to the returned function?

  • Where can one read about the details of what's going on here?

(Output:)

0
1
2
3
4
5
6
7
8
9
هل كانت مفيدة؟

المحلول

Perhaps you're confused by the outermost function that is being invoked immediately. This serves only to protect current in a variable scope. If we eliminate that, it's probably clearer.

var current = -1;
var next = function() {// This is what would have been returned in the original
    current += 1;
    if (current < 10) {
        console.log(current);

        next();       // recursive call 
    } else {
        return;
    }
};

next();

Now you have the same as the original code, except that current isn't in its own scope. As you can see, the function is simply assigned to the next variable.


That's exactly what's happening in the original, except that in the original the outer function exists and is immediately invoked. That outer function is a one-time-shot function, and is not assigned to next, though its return value is.


JavaScript doesn't have block scope (yet), but if it did, think of it as being similar to this:

var next;

{ // Create a (fictional) variable scope that has `current` and the function

    var current = -1;

    next = function() {
        current += 1;
        if (current < 10) {
            console.log(current);

            next();       // recursive call 
        } else {
            return;
        }
    };

}

next();

But since JS doesn't have block scope, but only function scope, we need to emulate this with a function.

We can tweak the original just a little to make it look similar.

var next;

(function() { // Create a variable scope that has `current` and the function

    var current = -1;

    next = function() {
        current += 1;
        if (current < 10) {
            console.log(current);

            next();       // recursive call 
        } else {
            return;
        }
    };

}());

next();

نصائح أخرى

During execution, how does the recursive next() call already refer to the returned function?

When you invoke the function to build the function which defines next (at the line with })(); // immediately call wrapper function), you are only returning a function (functions are just another kind of data in JavaScript until invoked) which references the global next variable, not yet utilizing it. The next step (next();) starts the process and by that time, the internal next reference can find the global next definition.

AFAIU, the terminology of "late binding" tends to have to do with the dynamic value of properties, with this being an especially important one.

Access to next is late here in the sense that the function doesn't need to be available at definition time, but rather at invocation time (though the variable next is already known to the function at definition time for the inner function as well, but its value is undefined at that time; the variable would be known to JavaScript even if ALL of your code had been inside a function and the var were set at the end of the block).

(A small note (which you can ignore if it is too much information): It is good practice (and necessary for "strict mode") to define globals like next with var as you have done. JavaScript will treat variable references as globals unless var is used within that scope, but as mentioned, even if all your code had been inside a function and next had been a local variable, its definition with var allows for discovery of that variable anywhere within the closure, even within nested functions (unlike this which is another can of worms).)

Where can one read about the details of what's going on here?

You might find this helpful: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures

UPDATE

In reply to a comment:

In JavaScript...

  1. If a function variable (or any kind of variable) is defined with var, it is recognized as undefined (not as a type error) in that scope (including the case where it is defined in the top, global scope), until an assignment is made to give it the function value (so it can be executed or passed around). Likewise with assignments on existing objects such as window.myFunc = function () {};.
  2. If a function is just declared without var like function myName () {}, it is immediately available everywhere in that scope, before or after the declaration. As with var function declarations, it can similarly be treated as data, with the function passed around as data, e.g., for callbacks.
  3. If a function variable is defined as a variable but without a var existing with that name anywhere up to the global scope, e.g., myGlobal = function () {};, even this will not produce errors (and will work like #1 above) unless "strict mode" is in effect, in which case it will produce an error.
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top