Когда на самом деле закрытие создается?

Действительно ли это верно, что закрытие создается в следующих случаях для foo, но не для bar?

Случай 1:

<script type="text/javascript">

    function foo() { }

</script>

foo закрытие с цепочкой объема только с глобальной областью видимости.

Случай 2:

<script type="text/javascript">

    var i = 1;
    function foo() { return i; }

</script>

то же как Случай 1.

Случай 3:

<script type="text/javascript">

    function Circle(r) {
        this.r = r;
    }
    Circle.prototype.foo = function() { return 3.1415 * this.r * this.r }

</script>

в этом случае, Circle.prototype.foo (который возвращается, область круга) относится к закрытию только с глобальной областью видимости. (это закрытие создается).

Случай 4:

<script type="text/javascript">

    function foo() { 
        function bar() { 
        }
    }

</script>

здесь, foo закрытие только с глобальной областью видимости, но bar (еще) не закрытие, потому что функция foo не вызывается в коде, таким образом, никакое закрытие bar когда-либо создается. Это будет только существовать если foo вызывается, и закрытие bar будет существовать до foo возвраты и закрытие bar будет затем собран "мусор", так как нет никакой ссылки на него вообще нигде.

Таким образом, то, когда функция не существует, не может быть вызвано, не может быть сослан, затем закрытие еще еще не существует (никогда не создаваемый). Только, когда функция может быть вызвана или может быть сослана, затем закрытие на самом деле создается?

13
задан Josh 6 May 2010 в 22:10
поделиться

4 ответа

панель закрытия будет существовать до тех пор, пока foo не вернется, а затем панель закрытия будет собрана мусором, поскольку нигде нет ссылки на нее

Да .

1
ответ дан 2 December 2019 в 01:31
поделиться

Замыкание - это когда свободные переменные в коде функции привязываются к некоторым значениям функцией «контекст» (замыкание здесь более подходящий термин, чем контекст).

<script type="text/javascript">
    var i = 1;
    function foo() { return i; }
</script>

Здесь i - это свободная переменная для кода функции foo . И эта свободная переменная не привязана к какому-либо конкретному значению каким-либо существующим контекстом (закрытие). Итак, у вас нет закрытия.

<script type="text/javascript">
    var i = 1;
    function foo() { return i; }
    foo(); // returns 1
    i = 2;
    foo(); // returns 2
</script>

Теперь, чтобы создать замыкание, вы должны предоставить контекст, ограничивающий значение:

<script type="text/javascript">

    function bar() {
       var i = 1;
       function foo() { return i; }
       return foo;
    }
    bar(); // returns function foo() { return i; }
    bar()(); // returns 1
    // no way to change the value of the free variable i => bound => closure
</script>

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

<script type="text/javascript">

    function bar() {
       var i = 1;
       function foo() { return i; }
       i = 2;
       return foo;
    }
    bar()(); // returns 2
</script>

Относительно ваших примеров:

  1. Случай 1 не является закрытием, это просто функция
  2. Случай 2 не является закрытием, это еще одна функция со свободной переменной
  3. Случай 3 не является закрытием, это еще другая функция со специальной «переменной» this . Когда функция вызывается как член объекта, объекту присваивается значение this . В противном случае значение this является глобальным объектом.
  4. Случай 4 - это не закрытие, это функция, определенная внутри другой функции. Если foo вернет bar , вы должны создать замыкание, содержащее только «bar» и его значение: function bar () {} .
8
ответ дан 2 December 2019 в 01:31
поделиться

Если я могу предложить модель того, когда и как создаются замыкания (это обсуждение является теоретическим, на самом деле интерпретатор может делать что угодно, пока поскольку конечный результат тот же): закрытие создается всякий раз, когда функция оценивается во время выполнения. Затем закрытие будет указывать на среду, в которой происходит выполнение. При загрузке сайта Javascript выполняется в порядке сверху вниз в глобальной среде. Все вхождения

function f(<vars>) {
  <body>
}

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

Так что же произошло, когда f () выполняется в глобальной среде? Мы можем думать об этом, во-первых, как о поиске в глобальной среде (где функция выполняется) для имени f . Мы обнаружили, что это указывает на закрытие. Чтобы выполнить замыкание, мы создаем новую среду, родительской средой которой является среда, на которую указывает замыкание f , то есть глобальная среда. В этой новой среде мы связываем аргументы f с его реальными значениями. Затем тело закрытия f выполняется в новой среде! Любая необходимая переменная f будет сначала разрешена в новой среде, которую мы только что создали.Если такой переменной не существует, мы рекурсивно находим ее в родительской среде, пока не попадем в глобальную среду. Любая создаваемая переменная f будет создана в новой среде.

Теперь давайте посмотрим на более сложный пример:

// At global level
var i = 10;                  // (1)
function make_counter(start) {
  return function() {
    var value = start++;
    return value;
  };
}                            // (2)
var count = make_counter(10);    // (3)
count();  // return 10       // (4)
count();  // return 11       // (5)
count = 0;                   // (6)

Происходит следующее:

В точке (1): создается ассоциация от i к 10 в глобальной среде (где var i = 10; выполняется.

В точке (2): закрытие выполняется с помощью переменной (start) и body return. ..; , который указывает на среду, в которой он выполняется (глобальный). Затем создается связь между make_counter и только что созданным замыканием.

В точке (3): Происходит несколько интересных вещей. Сначала мы обнаруживаем, с чем связан make_counter в глобальной среде. Затем мы выполняем это закрытие. Следовательно, создается новая среда, назовем ее CE , которая указывает в среду, на которую указывает закрытие make_counter (глобальный). Затем мы создаем ассоциацию от start до 10 в CE и запускаем тело закрытие make_counter в CE . Здесь мы сталкиваемся с другой функцией, которая является анонимной. Однако происходит то же самое, что и раньше (вспомните function f () {} эквивалентно var f = function () {}; ). Замыкание, назовем его count , создается с помощью переменной () (пустой список) и тела var ...возвращаемое значение; . Теперь это закрытие будет указывать на среду, в которой оно выполняется, то есть CE . Это будет очень важно позже. Наконец, у нас есть count указывает на новое закрытие в глобальной среде (Почему глобальное? Потому что var count ... выполняется в глобальной среде). Отметим, что CE не является сборщиком мусора, потому что мы можем получить доступ к CE через закрытие make_counter , которое мы можем получить из глобальной среды с помощью переменной ] make_counter .

В пункте (4) происходит более интересная вещь. Сначала мы находим замыкание, связанное с count , которое является только что созданным замыканием. Затем мы создаем новую среду, родителем которой является среда, на которую указывает замыкание, то есть CE ! Мы выполняем тело закрытия в этой новой среде. Когда выполняется var value = start ++; , мы ищем переменную start , начиная с текущей среды и последовательно продвигаясь вверх до глобальной среды. Мы нашли start в среде CE . Мы увеличиваем значение этого start , первоначально 10 до 11 . Теперь начало в CE указывает на значение 11 . Когда мы сталкиваемся с значением var , это означает, что не нужно искать существующее значение , а просто создать переменную в среде, где она выполняется.Таким образом, выполняется связь между значением и 11 . В возвращаемом значении ; мы ищем значение так же, как искали start . Оказывается, мы находим это в текущей среде,следовательно, нам не нужно просматривать родительские среды. Затем мы возвращаем это значение. Теперь новая среда, которую мы только что создали, будет обработана сборщиком мусора, так как мы больше не сможем добраться до этой среды по любому пути из global.

В точке (5) происходит то же самое, что и выше. Но теперь, когда мы ищем start , мы обнаружили, что значение равно 11 вместо 10 (в среде CE ).

В точке (6) мы повторно назначаем счетчик в глобальной среде. Мы обнаружили, что теперь мы больше не можем найти путь от глобального к закрытому счетчику и, в свою очередь, больше не можем найти путь к среде CE . Следовательно, оба они будут собраны мусором.

P.S. Для тех, кто знаком с LISP или Scheme, приведенная выше модель в точности совпадает с моделью среды в LISP / Scheme.

П.П.С. Ух, сначала хотел написать короткий ответ, но оказались настоящие бегемоты. Надеюсь, я не делаю вопиющей ошибки.

0
ответ дан 2 December 2019 в 01:31
поделиться

Ни в одном из этих примеров замыкание не создавалось.

Вторая создаст закрытие, если вы действительно создали функцию и что-то с ней сделали, теперь вы просто создаете функцию, а затем отбрасываете ее. То же, что и добавление строки 3 + 8; , вы создаете число, а затем отбрасываете его.

Замыкание - это просто функция, которая ссылается на переменные из своей среды создания в своем теле, каноническим примером является сумматор:

function createAdder(x) { //this is not a closure
    return function(y) { //this function is the closure however, it closes over the x.
        return y + x;
    }
} //thus createAdder returns a closure, it's closed over the argument we put into createAdder

var addTwo = createAdder(2);

addTwo(3); //3
0
ответ дан 2 December 2019 в 01:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: