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

Где работает закрытие JavaScript?

Я написал этот код, чтобы узнать о закрытии JavaScript:

function1 = function(){
  var variable = "foo"
  var function2 = function(argument){
    console.log(variable + argument);
  }
  return function2
}

function3 = function1();
function3("bar");

Это печатает "foobar", как ожидалось. Но где переменная живет?

Является ли это свойством функции3 или хранится где-то еще в функции3? Обращается ли JavaScript к какой-то замкнутой цепочке, подобно тому, как она проходит цепочку прототипов? Он хранится в памяти где-то еще?

Я пытаюсь понять это более глубоко.

4b9b3361

Ответ 1

TL;DR:

где находится переменная?

В среде он был определен в.

Является ли это свойством функции3 или хранится где-то еще в функции3?

Нет.

Просматривает ли JavaScript какую-то цепь замыкания, подобно тому, как она проходит цепочку прототипов?

Да.

Сохраняется ли она в памяти где-то еще?

Да.


tl; dr 2:

Функции сохраняют ссылку на среду, в которой они созданы. Когда вызывается функция, она создает новую среду, родитель которой является средой, в которой содержалась ссылка.


Более длинное объяснение:

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

Итак, когда мы оцениваем ваш пример кода, начальное состояние памяти (перед выполнением чего-либо) может выглядеть так (упрощенно):

+-(Global) lexical environment-+     +-Environment Record-+
+-------------+----------------+     +---------+----------+
| Environment |       *--------+---> |function1|undefined |
|   Record    |                |     +---------+----------+
+-------------+----------------+     |function3|undefined |
|    Outer    |                |     +---------+----------+
|   lexical   |    (empty)     |
| environment |                |
+-------------+----------------+

Глобальная среда не имеет внешней среды, поскольку она находится наверху. function1 и function3 - это две привязки, которые еще не были инициализированы (присвоение еще не было оценено).

После создания функции (оценка function1 = function() { ... }) память выглядит следующим образом:

            +------------------------------------------------------------------------+
            |                                                                        |
            v                                                                        |
+-(Global) lexical environment-+    +-Environment Record-+     +-----Function Object-+---+
+-------------+----------------+    +---------+----------+     +---------------+-----+---+
| Environment |       *--------+--->|function1|    *-----+---->|[[Environment]]|     *   |
|   Record    |                |    +---------+----------+     +---------------+---------+
+-------------+----------------+    |function3|undefined |     |     name      |function1|
|    Outer    |                |    +---------+----------+     +---------------+---------+
|   lexical   |    (empty)     |
| environment |                |
+-------------+----------------+

Теперь function1 имеет значение, объект функции. Объекты функции имеют несколько внутренних (например, [[Environment]]) и внешних (например, name) свойств. Как следует из названия, внутренние свойства не могут быть доступны из кода пользователя. Свойство [[Environment]] очень важно. Обратите внимание, как это относится к лексической среде, в которой была создана функция!

Следующий шаг выполняет function3 = function1(), то есть вызывает function2. Как я уже говорил в начале, всякий раз, когда выполняется функция, создается новая лексическая среда. Посмотрите на память сразу после ввода функции:

               +------------------------------------------------------------------------+
               |                                                                        |
               v                                                                        |
   +-(Global) lexical environment-+    +-Environment Record-+     +-----Function Object-+---+
   +-------------+----------------+    +---------+----------+     +---------------+-----+---+
   | Environment |       *--------+--->|function1|          +---->|[[Environment]]|     *   |
   |   Record    |                |    +---------+----------+     +---------------+---------+
+> +-------------+----------------+    |function3|undefined |     |     name      |function1|
|  |    Outer    |                |    +---------+----------+     +---------------+---------+
|  |   lexical   |    (empty)     |
|  | environment |                |
|  +-------------+----------------+
|
|
|
|  +-----lexical environment------+    +-Environment Record-+
|  +-------------+----------------+    +---------+----------+
|  | Environment |       *--------+--->|variable |undefined |
|  |   Record    |                |    +---------+----------+
|  +-------------+----------------+    |function2|undefined |
|  |    Outer    |                |    +---------+----------+
|  |   lexical   |        *       |
|  | environment |        |       |
|  +-------------+--------+-------+
|                         |
+-------------------------+

Это очень похоже на структуру глобальной среды! У нас есть лексическая среда, в которой есть запись об окружающей среде с двумя неинтеллизированными привязками. Но теперь большая разница заключается в том, что "внешняя лексическая среда" указывает на глобальную лексическую среду. Как это возможно?

При вызове function1 и создании новой лексической среды мы устанавливаем значение поля "внешняя лексическая среда" новой среды в значение поля function1 [[Environment]]. Это была создана цепочка областей.

Теперь, после выполнения function1, память имеет следующую структуру:

               +------------------------------------------------------------------------+
               |                                                                        |
               v                                                                        |
   +-(Global) lexical environment-+    +-Environment Record-+     +-----Function Object-+---+
   +-------------+----------------+    +---------+----------+     +---------------+-----+---+
   | Environment |       *--------+--->|function1|    *-----+---->|[[Environment]]|     *   |
   |   Record    |                |    +---------+----------+     +---------------+---------+
+> +-------------+----------------+    |function3|   |      |     |     name      |function1|
|  |    Outer    |                |    +---------+---+------+     +---------------+---------+
|  |   lexical   |    (empty)     |                  |
|  | environment |                |                  |
|  +-------------+----------------+                  +-------------------------+
|                                                                              |
|             +----------------------------------------------------------------+--------+
|             v                                                                |        |
|  +-----lexical environment------+    +-Environment Record-+                  v        |
|  +-------------+----------------+    +---------+----------+                           |
|  | Environment |       *--------+--->|variable |  'foo'   |     +-----Function Object-+---+
|  |   Record    |                |    +---------+----------+     +---------------+-----+---+
|  +-------------+----------------+    |function2|    *-----+---->|[[Environment]]|     *   |
|  |    Outer    |                |    +---------+----------+     +---------------+---------+
|  |   lexical   |        *       |                               |     name      |function2|
|  | environment |        |       |                               +---------------+---------+
|  +-------------+--------+-------+
|                         |
+-------------------------+

Аналогично function1, function2 имеет ссылку на среду, созданную вызовом function2. Кроме того, function3 относится к функции, которую мы создали, потому что мы возвращаем ее из function1.

Последний шаг: вызов function3('bar'):

               +------------------------------------------------------------------------+
               |                                                                        |
               v                                                                        |
   +-(Global) lexical environment-+    +-Environment Record-+     +-----Function Object-+---+
   +-------------+----------------+    +---------+----------+     +---------------+-----+---+
   | Environment |       *--------+--->|function1|    *-----+---->|[[Environment]]|     *   |
   |   Record    |                |    +---------+----------+     +---------------+---------+
+> +-------------+----------------+    |function3|   |      |     |     name      |function1|
|  |    Outer    |                |    +---------+---+------+     +---------------+---------+
|  |   lexical   |    (empty)     |                  |
|  | environment |                |                  |
|  +-------------+----------------+                  +-------------------------+
|                                                                              |
|             +----------------------------------------------------------------+--------+
|             v                                                                |        |
|  +-----lexical environment------+    +-Environment Record-+                  v        |
|  +-------------+----------------+    +---------+----------+                           |
|  | Environment |       *--------+--->|variable |  'foo'   |     +-----Function Object-+---+
|  |   Record    |                |    +---------+----------+     +---------------+-----+---+
|+>+-------------+----------------+    |function2|    *-----+---->|[[Environment]]|     *   |
|| |    Outer    |                |    +---------+----------+     +---------------+---------+
|| |   lexical   |        *       |                               |     name      |function2|
|| | environment |        |       |                               +---------------+---------+
|| +-------------+--------+-------+
++------------------------+
 |
 | +-----lexical environment------+    +-Environment Record-+
 | +-------------+----------------+    +---------+----------+
 | | Environment |       *--------+--->|argument |  'bar'   |
 | |   Record    |                |    +---------+----------+
 | +-------------+----------------+
 | |    Outer    |                |
 | |   lexical   |        *       |
 | | environment |        |       |
 | +-------------+--------+-------+
 +------------------------+

Аналогично здесь создается новая среда и поле "внешняя лексическая среда" указывает на среду, созданную при вызове function1.

Теперь поиск значения argument прост, потому что он существует в среде собственной записи. Но при поиске variable происходит следующее: поскольку он не существует в собственной записи среды, он смотрит на свою запись "внешняя лексическая среда". Он может это сделать, потому что имеет ссылку на него.

Ответ 2

Всякий раз, когда JavaScript выполняет функцию function3, создается объект "scope" для хранения локальной переменной, названной вами как переменной ( "foo" ). Обратите внимание, что ваш код JavaScript не может напрямую обращаться к этому объекту области видимости. И, таким образом, значение "foo" доступно для внутренней функции, хотя внешняя функция вернулась.

Просматривает ли JavaScript какую-то цепь замыкания, подобно тому, как она проходит цепочку прототипов?

Да. "Объекты Scope образуют цепочку, называемую цепочкой областей видимости, аналогичную цепочке прототипов, используемой объектной системой JavaScript.

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

Подробнее здесь:

Ответ 3

Переменные живут в области, в которой они объявлены, которая является глобальной или функцией.

Ключевое слово здесь область.

Как объясняется блестяще на веб-сайте MSDN:

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

ИЗМЕНИТЬ

На самом деле это немного сложнее, чем это, см. toddmotto сообщение о областях JS.

Ответ 4

Закрытие объясняется на веб-сайте MDN. Но я бы рекомендовал пройти через это удивительное объяснение в ответе "Переполнение стека" , точный ответ на ваш вопрос.

Это не свойство. Просто, что память запоминается в памяти. Окружающая среда означает функцию, а также сферу действия и сферу охвата. Надеюсь, он просветит путь.

Ответ 5

Для объяснения того, как работают замыкания, см. этот ответ.

Как работают блокировки JavaScript?

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

Для получения более подробной информации см., например, эти две большие части: