Подтвердить что ты не робот

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

Верно ли, что замыкание создается в следующих случаях для 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 будет собрано в мусор, поскольку ссылки на него вообще нет.

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

4b9b3361

Ответ 1

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

<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 не является замыканием, это просто функция
  • Случай 2 не является замыканием, это другая функция со свободной переменной
  • Случай 3 не является замыканием, это еще одна функция со специальной "переменной" this. Когда функция вызывается как член объекта, объект присваивается значению this. В противном случае значением this является глобальный объект.
  • Случай 4 не является замыканием, это функция, определенная внутри другой функции. Если foo return bar, вы создадите закрытие, которое содержит только "bar" и его значение: function bar() {}.

Ответ 2

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

Да.

Ответ 3

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

Второй создаст закрытие, если вы на самом деле создали функцию и что-то с ней сделали, теперь вы просто создаете функцию, а затем выбросите ее. То же, что и добавление строки 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 closed over the argument we put into createAdder

var addTwo = createAdder(2);

addTwo(3); //3

Ответ 4

Если я могу предложить модель о том, когда и как создаются замыкания (это обсуждение является теоретическим, на самом деле интерпретатор может делать что угодно, если конечный результат одинаков): замыкание создается всякий раз, когда функция оценивается во время выполнение. Затем закрытие укажет на среду, в которой выполняется выполнение. Когда загрузка сайта, 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 ... return value;. Теперь это закрытие будет указывать на среду, в которой он выполняется, т.е. CE. Это будет очень важно позже. Наконец, мы имеем count указывает на новое замыкание в глобальной среде (почему глобально? Потому что var count ... выполняется в глобальной среде). Заметим, что CE не собирает мусор, потому что мы можем достичь CE через замыкание make_counter, которое мы можем получить из глобальной среды из переменной make_counter.

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

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

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

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

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

Ответ 5

На самом деле, после нескольких лет использования JavaScript и достаточно тщательных исследований, у меня теперь есть лучший ответ:

Всякий раз, когда функция возникает, создается замыкание.

Поскольку функция является всего лишь объектом, мы можем более точно сказать: всякий раз, когда экземпляр объекта Function (экземпляр функции возникает), создается замыкание.

Итак,

function foo() { }

Когда JS завершает работу над указанной линией, есть уже закрытие или

var fn = function() { };

или

return function() { return 1; };

Почему? Поскольку замыкание - это просто функция с цепочкой областей видимости, поэтому в каждой ситуации выше существовала функция (она появилась. Вы можете ее вызвать (вызвать)). У него также был объем. Поэтому в моем первоначальном вопросе (я был OP), каждый случай с 1 по 4, в каждом отдельном случае было создано закрытие.

Случай 4 - интересный случай. После выполнения этого кода происходит замыкание из-за появления foo(), но bar() еще не существует (без вызова foo()), поэтому было создано одно закрытие, а не два.