Более глубокое понимание закрытия в Javascript

Я читал комментарии к ответу и видел этот комментарий:

[закрытие] не сохраняет состояние foo так же, как создает специальную область, содержащую (1) возвращаемую функцию, и (2) все внешние переменные, на которые ссылаются во время возврата. Эта специальная область называется замыканием.

Хорошо, пока все хорошо. Теперь вот интересная часть, о которой я не знал:

Случай в точке... если бы у вас был другой var, определенный в foo, который был не указан в функции return, он не существовал бы в области закрытия.

Я предполагаю, что это имеет смысл, но какие последствия имеет это помимо использования/производительности памяти?

Вопрос. Если бы все переменные в области были включены в закрытие, что бы это сделало, что я не могу сделать с текущей моделью?

Ответ 1

Я думаю, что вы слишком внимательно относитесь к этому комментарию. Комментарий просто говорит о том, что вы не можете получить доступ к нему за пределами области действия (он не является общедоступным), а не то, что он вообще не доступен внутри функции. Возвращенная функция будет иметь доступ ко всем областям внешних функций независимо от того, что. Вы просто не можете получить доступ к этой области вне внешней функции, если внутренняя функция не предоставляет способ доступа к ней.

Например, это выражение оценивается как 4:

function testClosure(){
 var x = 2;
    return function(y){
        alert(eval(y));
    }

}

var closure = testClosure();

closure("x+2");  //4

http://jsfiddle.net/dmRcH/

Итак, x доступен, несмотря на то, что он не связан напрямую

Дальнейшие исследования

Похоже, что chrome и firefox по крайней мере пытаются оптимизировать это в том смысле, что если вы не предоставляете ЛЮБОЙ способ ссылаться на переменную x, она не отображается как доступная в отладчике. Выполняя это с точкой останова внутри закрытия, показывается x как недоступный в Chrome 26 и Firefox 18.

http://jsfiddle.net/FgekX/1/

Но это просто детали управления памятью, а не соответствующее свойство языка. Если есть возможный способ ссылки на переменную, он передается, и мое подозрение в том, что другие браузеры могут не оптимизировать это таким же образом. Всегда лучше кодировать спецификацию, а не реализацию. В этом случае, хотя правило действительно: ", если есть какой-либо возможный способ доступа к нему, он будет доступен. А также не использовать eval, потому что это действительно поможет вашему коду оптимизировать что-либо.

Ответ 2

если у вас был другой var, определенный в foo, на который не ссылался функция возврата, он не существовал бы в области закрытия.

Это не совсем точно; переменная является частью области закрытия, даже если она не может быть напрямую указана внутри самой функции (смотря на код функции). Разница заключается в том, как двигатели оптимизируют неиспользуемые переменные.

Например, неиспользуемые переменные в области закрытия, как известно, вызывают утечку памяти (в некоторых системах) при работе с элементами DOM. Возьмите этот классический пример, например:

function addHandler() {
    var el = document.getElementById('el');
    el.onclick = function() {
        this.style.backgroundColor = 'red';
    }
}

Источник

В вышеприведенном коде происходит утечка памяти (как в IE, так и в Mozilla по крайней мере), потому что el является частью области закрытия для функции обработчика кликов, хотя на нее не ссылаются; это вызывает циклическую ссылку, которую невозможно удалить, поскольку их сбор мусора основан на подсчете ссылок.

Chrome, с другой стороны, использует другой сборщик мусора:

В V8 куча объекта разделена на две части: новое пространство, в котором создаются объекты, и старое пространство, в которое продвигаются объекты, переживающие цикл сбора мусора. Если объект перемещается в цикле сбора мусора, V8 обновляет все указатели на объект.

Это также называется генераторным или эфемерным сборщиком мусора. Хотя это сложнее, этот тип сборщика мусора может более точно установить, используется ли переменная или нет.

Ответ 3

JavaScript не имеет неотъемлемого смысла конфиденциальности, поэтому мы используем функцию scope (закрытие) для имитации этой функциональности.

Ответ SO, на который вы ссылаетесь, является примером шаблона модуля, который Адди Османи делает отличную работу, объясняя важность Изучение шаблонов проектирования JavaScript

Схема модуля инкапсулирует " конфиденциальность", состояние и организация используя затворы. Он обеспечивает способ объединения частные методы и переменные, защищая части от утечки в глобальный охват и случайное столкновение с интерфейс. С помощью этого шаблона возвращается только открытый API, сохраняя все остальное в закрытии закрыто.

Ответ 4

    Please have a look below code:

    for(var i=0; i< 5; i++){            
                setTimeout(function(){
                    console.log(i);
                },1000);                        
            }

   Here what will be output? 0,1,2,3,4 not that will be 5,5,5,5,5 because of closure

   So how it will solve? Answer is below:

   for(var i=0; i< 5; i++){
            (function(j){     //using IIFE           
                setTimeout(function(){
                    console.log(j);
                },1000);
            })(i);          
        }

    Let me simple explain, when a function created nothing happen until it called so for loop in 1st code called 5 times but not called immediately so when it called i.e after 1 second and also this is asynchronous so before this for loop finished and store value 5 in var i and finally execute setTimeout function five time and print 5,5,5,5,5

Here how it solve using IIFE i.e Immediate Invoking Function Expression

   (function(j){  //i is passed here           
                setTimeout(function(){
                    console.log(j);
                },1000);
            })(i);  //look here it called immediate that is store i=0 for 1st loop, i=1 for 2nd loop, and so on and print 0,1,2,3,4

For more, please understand execution context to understand closure.

- There is one more solution to solve this using let (ES6 feature) but under the hood above function is worked

 for(let i=0; i< 5; i++){           
                setTimeout(function(){
                    console.log(i);
                },1000);                        
            }

Output: 0,1,2,3,4