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