Закрытие Javascript, возвращающее рекурсивную функцию

Я новичок в Javascript и функциональных парадигмах. Мне действительно нравится использовать закрытие, чтобы безопасно хранить маленькие кусочки состояния в закрытой области видимости. Это освежающее изменение от песни и танца поклонения классу на Яве.

Я написал следующий код с намерением вывести на консоль 0-9. Это работает, но я удивлен, что это делает.

Я не понимаю как next ссылается на возвращенную функцию для рекурсивного next() вызов! Связано ли это со свойством "позднего связывания" 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
  • Как во время выполнения рекурсивный вызов next() уже ссылается на возвращаемую функцию?

  • Где можно прочитать о деталях того, что здесь происходит?

(Выход:)

0
1
2
3
4
5
6
7
8
9

2 ответа

Решение

Возможно, вы смущены самой внешней функцией, которая вызывается немедленно. Это служит только для защиты current в переменной области. Если мы устраняем это, это, вероятно, яснее.

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();

Теперь у вас есть то же, что и оригинальный код, за исключением того, что current не в своей сфере. Как видите, функция просто назначена next переменная.


Это именно то, что происходит в оригинале, за исключением того, что в оригинале внешняя функция существует и немедленно вызывается. Эта внешняя функция является одноразовой и не назначается nextхотя его return значение


У JavaScript нет пока блочной области видимости, но если он есть, подумайте, что он похож на это:

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();

Но поскольку JS не имеет области видимости блока, а только области действия функции, нам нужно эмулировать это с помощью функции.

Мы можем немного подправить оригинал, чтобы он выглядел похожим.

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();

Как во время выполнения рекурсивный вызов next() уже ссылается на возвращаемую функцию?

Когда вы вызываете функцию, чтобы построить функцию, которая определяет next (в соответствии с })(); // immediately call wrapper function), вы только возвращаете функцию (функции - это просто другой тип данных в JavaScript, пока не будет вызван), которая ссылается на глобальный next переменная, еще не используя ее. Следующий шаг (next();) начинает процесс и к тому времени внутренний next ссылка может найти глобальный next определение.

AFAIU, терминология "позднего связывания" имеет тенденцию иметь отношение к динамическому значению свойств, с this будучи особенно важным.

Доступ к next опаздывает здесь в том смысле, что функция не должна быть доступна во время определения, а скорее во время вызова (хотя переменная next функция уже известна во время определения для внутренней функции, но ее значение undefined в это время; переменная была бы известна JavaScript, даже если бы ВСЕ ваш код был внутри функции и var были установлены в конце блока).

(Небольшое примечание (которое можно игнорировать, если в нем слишком много информации): Хорошая практика (и необходимая для "строгого режима") - определять глобальные переменные, такие как next с var как вы сделали. JavaScript будет обрабатывать ссылки на переменные как глобальные var используется в этой области, но, как уже упоминалось, даже если весь ваш код был внутри функции и next была локальной переменной, ее определение с var позволяет обнаруживать эту переменную в любом месте внутри замыкания, даже внутри вложенных функций (в отличие от this это еще одна банка глистов).)

Где можно прочитать о деталях того, что здесь происходит?

Вы можете найти это полезным: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures

ОБНОВИТЬ

В ответ на комментарий:

В JavaScript...

  1. Если переменная функции (или любая переменная) определяется с помощью varэто признается undefined (не как ошибка типа) в этой области (включая случай, когда она определена в верхней части, глобальная область), пока не будет выполнено присваивание, чтобы дать ему значение функции (чтобы его можно было выполнить или передать). Аналогично с назначениями на существующие объекты, такие как window.myFunc = function () {};,
  2. Если функция только что объявлена ​​без var лайк function myName () {}, он сразу доступен везде в этой области, до или после объявления. Как с var объявления функций, они также могут рассматриваться как данные, а функция передается как данные, например, для обратных вызовов.
  3. Если переменная функции определена как переменная, но без var существующий с таким именем в любом месте вплоть до глобальной области видимости, например, myGlobal = function () {};даже это не приведет к ошибкам (и будет работать как #1 выше), если не действует "строгий режим", и в этом случае он выдаст ошибку.
Другие вопросы по тегам