IIFE как закрытие

В Дон 't Know Javascript, 1/3 IIFE описываются как не закрывающиеся сами по себе, а только в том случае, если они выполняются за пределами своей лексической области:

В главе 3 был представлен паттерн IIFE. Хотя часто говорят, что IIFE (отдельно) является примером наблюдаемого закрытия, я бы несколько не согласился с нашим определением, приведенным выше.

Этот код «работает», но это не строго наблюдение за закрытием. Почему? Потому что функция (которую мы здесь назвали «IIFE») не выполняется вне своей лексической области видимости. Он по-прежнему вызывается прямо там, в той же области, в которой он был объявлен (охватывающая / глобальная область, которая также содержит a). a можно найти с помощью обычного поиска в лексической области видимости, а не с помощью замыкания.

var a = 2;

(function IIFE(){ // not actually a "closure"
    console.log( a ); 
})();

В этом сообщении SO следующий фрагмент был приведен в качестве примера закрытия:

for (var i = 0; i < someVar.length; i++)
    (function (i) {
        window.setTimeout(function () { 
            alert("Value of i was "+i+" when this timer was set" )
        }, 10000);
    })(i); 

Я пытаюсь понять это с точки зрения определения закрытия (как определено в эта средняя статья):

Чтобы использовать замыкание, просто определите функцию внутри другой функции и откройте ее. Чтобы открыть функцию, верните ее или передайте другой функции. ...

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

Я понимаю, что закрытие - это "функция с отслеживанием состояния", и что это

способ «запомнить» и продолжить доступ к области видимости функции (ее переменным) даже после того, как функция завершила выполнение.

Итак, в этом примере я вижу, что i цикла запоминается при передаче в закрывающий IIFE.

Мой вопрос:

Где происходит «переход к другой функции или возврат»? Я предполагаю, что IIFE может запоминать внешнее значение цикла i for на каждой итерации, потому что IIFE передается в окно?

В принципе, я понимаю, что закрытие определяется как запоминание значения внешней области после того, как сборщик мусора очищает эту внешнюю область, и что его использование заключается в том, чтобы раскрыть закрытие, возвращая его и обращаясь к нему за пределами его лексической области видимости. Это правильно? Так где же происходит «доступ к нему за пределами лексической области»?


person Growler    schedule 17.07.2017    source источник
comment
Лучшим примером замыкания IIFE будет let plus3 = (x => y => x + y)(3);. Здесь переменная x закрыта. Когда вы вызываете его со значением 3, он возвращает функцию, которая принимает другой аргумент y и добавляет к нему 3.   -  person Jared Smith    schedule 17.07.2017
comment
Я не уверен, что закрытие становится не-закрытием просто в зависимости от того, когда / где оно вызывается ...   -  person Oliver Charlesworth    schedule 17.07.2017
comment
@OliverCharlesworth Я думаю, что автор, цитируемый OP, имел в виду что-то вроде того, когда он фактически не закрывает что-либо интересное в окружающей области.   -  person Jared Smith    schedule 17.07.2017
comment
@growler window не имеет к этому никакого отношения, все анонимные функции, переданные в setTimeout, закрываются по переменной i в цикле.   -  person Jared Smith    schedule 17.07.2017
comment
Возможный дубликат Как работают закрытия JavaScript?   -  person Jared Smith    schedule 17.07.2017
comment
@JaredSmith да, я понимаю это. Но вопрос в том, что если анонимные функции, переданные в setTimeout, закрываются над переменной i, почему это не сохраняется i? Обертка IIFE ((function (i) { ... })(i)) создает более закрытую область, которая каким-то образом сохраняет i после завершения цикла. Это потому, что упаковка IIFE выполняется в другом объеме?   -  person Growler    schedule 17.07.2017
comment
В примере (function (i) { ... })(i) целью этого IIFE является создание локальной (аргументной) переменной, чтобы каждый из обратных вызовов setTimeout ссылался на другую локальную переменную с именем i вместо того же глобального i. Функция, переданная в setTimeout clsoes, вместо i, созданного аргументом с именем i в окружающем IIFE.   -  person apsillers    schedule 17.07.2017
comment
@growler i сохраняется. Это должно быть, потому что setTimeout делает выполнение асинхронным. Без IIFE для создания замыкания i будет любым значением при фактическом вызове обратного вызова, в этом случае он будет someVar.length - 1 снова и снова, а не увеличивать значения. Вставьте for (var i=0; i<10; ++i) setTimeout(function() { console.log(i) }) в консоль.   -  person Jared Smith    schedule 17.07.2017
comment
@apsillers Я думаю, что понял. Поскольку это анонимная функция, она помещается за пределы цикла обработки событий до завершения for loop (в этот момент i === someVar.length-1). Когда стек цикла событий очищен, setTimeout выполняется и возвращается ... без IIFE, анонимная функция setTimeout все еще закрывается через i, но уже после завершения цикла событий. IIFE вызывается для каждой итерации i и создает локальную переменную значения i.   -  person Growler    schedule 17.07.2017
comment
Таким образом, с IIFE или без него анонимная функция setTimeout по-прежнему закрывается более i. Мы просто хотим закрыть правильный локальный i из выполненного IIFE   -  person Growler    schedule 17.07.2017
comment
@Growler да, это правильно. Извините, если я не смог передать это ясно, рад, что апсиллеру досталось это для вас.   -  person Jared Smith    schedule 17.07.2017
comment
@JaredSmith имеет смысл. Спасибо!   -  person Growler    schedule 17.07.2017


Ответы (1)


в этом примере я вижу, что i цикла запоминается при передаче в закрывающий IIFE

Нет. IIFE предоставляет только возможность запоминания значения i. Как видно из приведенных вами цитат, функция IIFE не является закрытием. Выражение функции, которое использует i, является закрытием:

(function IIFE(i) {
    // this is the scope of i

    window.setTimeout(function closure() {
        // this function closes over i
        alert("Value of my local 'i' is still "+i+" even after 10 seconds");
    }, 10000);
})(0);
// ^ this is some arbitrary value passed to the IIFE - it could have been a loop variable

Где происходит «переход к другой функции или возврат»?

Это функция closure, переданная в setTimeout, которая вызовет ее обратно из места, где i обычно больше не определяется - если бы это не было закрытием.

person Bergi    schedule 17.07.2017
comment
Спасибо тебе за это. Я уже разобрался в комментариях под моим вопросом :) - person Growler; 18.07.2017