0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<h2>Важное примечание</h2>
1
<h2>Важное примечание</h2>
2
<p>Изначально в начале конспекта и урока речь шла о глобальном и локальном окружении, но объяснялась фактически глобальная и локальная область видимости. В видео эта тема до сих пор называется "глобальное и локальное окружение", текстовая версия урока исправлена.</p>
2
<p>Изначально в начале конспекта и урока речь шла о глобальном и локальном окружении, но объяснялась фактически глобальная и локальная область видимости. В видео эта тема до сих пор называется "глобальное и локальное окружение", текстовая версия урока исправлена.</p>
3
<p>Прежде, чем приступить к уроку, разберёмся с терминологией:</p>
3
<p>Прежде, чем приступить к уроку, разберёмся с терминологией:</p>
4
<ul><li>Область видимости (scope) - это широкое понятие, означающее, грубо говоря, "интерпретатор в разных местах кода видит разные штуки".</li>
4
<ul><li>Область видимости (scope) - это широкое понятие, означающее, грубо говоря, "интерпретатор в разных местах кода видит разные штуки".</li>
5
<li>Лексическая область видимости (Lexical scoping) - это конкретный механизм, одно из правил для области видимости. Этот механизм применяется в JavaScript и большинстве других языков. Под лексической областью видимости можно понимать просто механизм поиска значений: смотрим в текущей области, если нет - идём на уровень выше, и так далее. Слово "лексический" означает, что видимость задаётся исключительно текстом программы, исходным кодом. То есть можно смотреть на программу, не запуская её, и понять область видимости в любой точке. В других языках может быть не лексический механизм, а динамический (<a>dynamic scope</a>).</li>
5
<li>Лексическая область видимости (Lexical scoping) - это конкретный механизм, одно из правил для области видимости. Этот механизм применяется в JavaScript и большинстве других языков. Под лексической областью видимости можно понимать просто механизм поиска значений: смотрим в текущей области, если нет - идём на уровень выше, и так далее. Слово "лексический" означает, что видимость задаётся исключительно текстом программы, исходным кодом. То есть можно смотреть на программу, не запуская её, и понять область видимости в любой точке. В других языках может быть не лексический механизм, а динамический (<a>dynamic scope</a>).</li>
6
<li>Окружение (environment) - это область памяти, где записываются идентификаторы и значения из областей видимости. Не путайте с окружением, как средой исполнения.</li>
6
<li>Окружение (environment) - это область памяти, где записываются идентификаторы и значения из областей видимости. Не путайте с окружением, как средой исполнения.</li>
7
</ul><p>Путаница возникает ещё потому, что в разделе про Область видимости мы смотрим на примеры, которые работают по правилу "лексическая область видимости". От этого никуда не деться, так как язык JavaScript работает именно так.</p>
7
</ul><p>Путаница возникает ещё потому, что в разделе про Область видимости мы смотрим на примеры, которые работают по правилу "лексическая область видимости". От этого никуда не деться, так как язык JavaScript работает именно так.</p>
8
<h2>Транскрипт урока</h2>
8
<h2>Транскрипт урока</h2>
9
<h3>Часть I. Окружение</h3>
9
<h3>Часть I. Окружение</h3>
10
<p>Давайте поговорим об окружении. Наша планета огромна, но мы все делим её. Если вы построите химический завод, неплохо бы изолировать его от окружающего мира, чтобы то, что в нём происходит оставалось внутри. Вы можете сказать, что в этом здании своё окружение, микроклимат, изолированный от внешней окружающей среды. В программировании такая изоляция называется областью видимости.</p>
10
<p>Давайте поговорим об окружении. Наша планета огромна, но мы все делим её. Если вы построите химический завод, неплохо бы изолировать его от окружающего мира, чтобы то, что в нём происходит оставалось внутри. Вы можете сказать, что в этом здании своё окружение, микроклимат, изолированный от внешней окружающей среды. В программировании такая изоляция называется областью видимости.</p>
11
<p>Ваша программа имеет подобную структуру по похожим причинам. То, что вы создаёте снаружи - снаружи функций, инструкций if, циклов и других блоков кода - находится во внешней, глобальной области видимости.</p>
11
<p>Ваша программа имеет подобную структуру по похожим причинам. То, что вы создаёте снаружи - снаружи функций, инструкций if, циклов и других блоков кода - находится во внешней, глобальной области видимости.</p>
12
<p>Константа age , функция multiplier и переменная result - имеют "глобальную область видимости". Область видимости означает "область, где компоненты доступны".</p>
12
<p>Константа age , функция multiplier и переменная result - имеют "глобальную область видимости". Область видимости означает "область, где компоненты доступны".</p>
13
<p>Внутри функции multiplier есть константа x. Поскольку она внутри блока кода, это локальная константа, а не глобальная. Она видна только внутри этой функции, но не снаружи. У неё локальная область видимости.</p>
13
<p>Внутри функции multiplier есть константа x. Поскольку она внутри блока кода, это локальная константа, а не глобальная. Она видна только внутри этой функции, но не снаружи. У неё локальная область видимости.</p>
14
<p>В функции multiplier есть ещё один компонент из локальной области видимости - аргумент num. Он не задан так же чётко, как константы или переменные, но ведёт себя почти как локальная переменная.</p>
14
<p>В функции multiplier есть ещё один компонент из локальной области видимости - аргумент num. Он не задан так же чётко, как константы или переменные, но ведёт себя почти как локальная переменная.</p>
15
<p>У нас нет доступа к x снаружи, как будто её там не существует:</p>
15
<p>У нас нет доступа к x снаружи, как будто её там не существует:</p>
16
<p>console.log вызывается в глобальном окружении, а x не задан в этом глобальном окружении. Поэтому мы получаем Reference Error.</p>
16
<p>console.log вызывается в глобальном окружении, а x не задан в этом глобальном окружении. Поэтому мы получаем Reference Error.</p>
17
<p>Мы можем задать x глобально:</p>
17
<p>Мы можем задать x глобально:</p>
18
<p>Теперь существует глобальный x и его значение было выведено на экран, но локальный x внутри функции multiplier по-прежнему виден только внутри этой функции. Эти два x не имеют ничего общего друг с другом, они находятся в разных областях видимости. Они не схлопываются в одно целое, несмотря на то, что у них одно и то же имя.</p>
18
<p>Теперь существует глобальный x и его значение было выведено на экран, но локальный x внутри функции multiplier по-прежнему виден только внутри этой функции. Эти два x не имеют ничего общего друг с другом, они находятся в разных областях видимости. Они не схлопываются в одно целое, несмотря на то, что у них одно и то же имя.</p>
19
<p>Любой блок кода между фигурными скобками имеет локальную область видимости. Вот пример с блоком if:</p>
19
<p>Любой блок кода между фигурными скобками имеет локальную область видимости. Вот пример с блоком if:</p>
20
<p>То же работает для циклов while и for.</p>
20
<p>То же работает для циклов while и for.</p>
21
<p>Ок, локальное не доступно снаружи. Но глобальное доступно везде. Даже внутри чего-то? Да!</p>
21
<p>Ок, локальное не доступно снаружи. Но глобальное доступно везде. Даже внутри чего-то? Да!</p>
22
<p>Эта глобальная переменная a изменилась внутри функции changer. Функция вообще выполняет что-то только когда её вызывают, а не когда её определяют, так что вначале a это 0, но после того как вызывается changer, a становится 1.</p>
22
<p>Эта глобальная переменная a изменилась внутри функции changer. Функция вообще выполняет что-то только когда её вызывают, а не когда её определяют, так что вначале a это 0, но после того как вызывается changer, a становится 1.</p>
23
<p>Хоть это и заманчиво всё помещать в глобальную область видимости и забыть о сложностях областей видимости - это ужасная практика. Глобальные переменные делают ваш код невероятно хрупким. В таком случае что угодно может сломать что угодно. Поэтому избегайте глобальной области видимости, храните вещи там, где им место.</p>
23
<p>Хоть это и заманчиво всё помещать в глобальную область видимости и забыть о сложностях областей видимости - это ужасная практика. Глобальные переменные делают ваш код невероятно хрупким. В таком случае что угодно может сломать что угодно. Поэтому избегайте глобальной области видимости, храните вещи там, где им место.</p>
24
<h3>Часть II. Лексическая область видимости</h3>
24
<h3>Часть II. Лексическая область видимости</h3>
25
<p>Взгляните на эту программу:</p>
25
<p>Взгляните на эту программу:</p>
26
<p>Функция multiplier возвращает произведение a и b. a задано внутри, а b - нет.</p>
26
<p>Функция multiplier возвращает произведение a и b. a задано внутри, а b - нет.</p>
27
<p>Пытаясь решить умножение a * b, JavaScript ищет значения a и b. Он начинает искать локально и выходит наружу, по одной области видимости за шаг, пока он не найдёт то, что ему нужно или пока не поймёт, что это невозможно найти.</p>
27
<p>Пытаясь решить умножение a * b, JavaScript ищет значения a и b. Он начинает искать локально и выходит наружу, по одной области видимости за шаг, пока он не найдёт то, что ему нужно или пока не поймёт, что это невозможно найти.</p>
28
<p>Поэтому в данном примере JavaScript начинает с поиска a внутри локальной области видимости - внутри функции multiplier. Он находит значение сразу и переходит к b. Невозможно найти значение b в локальной области видимости, поэтому он переходит к наружной области. Тут он находит b - это 10. a * b превращается в 5 * 10, а затем в 50.</p>
28
<p>Поэтому в данном примере JavaScript начинает с поиска a внутри локальной области видимости - внутри функции multiplier. Он находит значение сразу и переходит к b. Невозможно найти значение b в локальной области видимости, поэтому он переходит к наружной области. Тут он находит b - это 10. a * b превращается в 5 * 10, а затем в 50.</p>
29
<p>Весь этот кусок кода мог бы быть внутри другой функции, и ещё внутри другой функции. И если бы b не нашлась здесь, JavaScript продолжил бы искать b за пределами функции, слой за слоем.</p>
29
<p>Весь этот кусок кода мог бы быть внутри другой функции, и ещё внутри другой функции. И если бы b не нашлась здесь, JavaScript продолжил бы искать b за пределами функции, слой за слоем.</p>
30
<p>Заметьте, что a = 7 не затронула вычисления, a была найдена внутри, поэтому внешняя a не сыграла роли.</p>
30
<p>Заметьте, что a = 7 не затронула вычисления, a была найдена внутри, поэтому внешняя a не сыграла роли.</p>
31
<p>Этот механизм называется лексической областью видимости. Область видимости любого компонента определяется местом расположения этого компонента внутри кода. И вложенные блоки имеют доступ к их внешним областям видимости.</p>
31
<p>Этот механизм называется лексической областью видимости. Область видимости любого компонента определяется местом расположения этого компонента внутри кода. И вложенные блоки имеют доступ к их внешним областям видимости.</p>
32
<h3>Часть III. Замыкания</h3>
32
<h3>Часть III. Замыкания</h3>
33
<p>Большинство языков программирования имеют что-то вроде области видимости или окружения, и этот механизм позволяет существовать замыканиям. Замыкание - это всего лишь модное название функции, которая запоминает внешние штуки, используемые внутри.</p>
33
<p>Большинство языков программирования имеют что-то вроде области видимости или окружения, и этот механизм позволяет существовать замыканиям. Замыкание - это всего лишь модное название функции, которая запоминает внешние штуки, используемые внутри.</p>
34
<p>Перед тем, как мы продолжим, давайте вспомним, как функции создаются и используются:</p>
34
<p>Перед тем, как мы продолжим, давайте вспомним, как функции создаются и используются:</p>
35
<p>f - довольно бесполезная функция, она всегда возвращает 0. Весь этот набор состоит из двух частей: константы и самой функции.</p>
35
<p>f - довольно бесполезная функция, она всегда возвращает 0. Весь этот набор состоит из двух частей: константы и самой функции.</p>
36
<p>Важно помнить, что эти два компонента раздельны. Первый - константа с именем f. Её значение могло бы быть числом или строкой. Но в данном случае её значение - функция.</p>
36
<p>Важно помнить, что эти два компонента раздельны. Первый - константа с именем f. Её значение могло бы быть числом или строкой. Но в данном случае её значение - функция.</p>
37
<p>Мы использовали аналогию в предыдущих уроках: константы как листы бумаги - имя на одной стороне, значение на другой. Следовательно, f - лист бумаги с f на одной стороне и описанием запускаемой функции на другой.</p>
37
<p>Мы использовали аналогию в предыдущих уроках: константы как листы бумаги - имя на одной стороне, значение на другой. Следовательно, f - лист бумаги с f на одной стороне и описанием запускаемой функции на другой.</p>
38
<p>Когда вы вызываете эту функцию, вот так:</p>
38
<p>Когда вы вызываете эту функцию, вот так:</p>
39
<p>… создаётся новый ящик, основываясь на описании на этом листе бумаги.</p>
39
<p>… создаётся новый ящик, основываясь на описании на этом листе бумаги.</p>
40
<p>Ок, вернёмся к замыканиям. Рассмотрим следующий код:</p>
40
<p>Ок, вернёмся к замыканиям. Рассмотрим следующий код:</p>
41
<p>Функция createPrint создаёт константу name и затем функцию с именем printName. Обе они локальные для функции createPrint, и доступны только внутри createPrint.</p>
41
<p>Функция createPrint создаёт константу name и затем функцию с именем printName. Обе они локальные для функции createPrint, и доступны только внутри createPrint.</p>
42
<p>У самой printName нет локальных компонентов, но у неё есть доступ к области видимости, где она сама находится, внешней области, где задана константа name.</p>
42
<p>У самой printName нет локальных компонентов, но у неё есть доступ к области видимости, где она сама находится, внешней области, где задана константа name.</p>
43
<p>Затем функция createPrint возвращает функцию printName. Помните, определения функций - это описания запущенных функций, просто фрагменты информации, как числа или строки. Поэтому мы можем вернуть определение функции, как мы возвращаем число.</p>
43
<p>Затем функция createPrint возвращает функцию printName. Помните, определения функций - это описания запущенных функций, просто фрагменты информации, как числа или строки. Поэтому мы можем вернуть определение функции, как мы возвращаем число.</p>
44
<p>Во внешней области видимости мы создаём константу myPrint и задаём ей значение, которое возвращает вызов функции createPrint(). Этот вызов возвращает функцию, так что теперь myPrint - это функция. Вызовите её, и на экран выведется "King".</p>
44
<p>Во внешней области видимости мы создаём константу myPrint и задаём ей значение, которое возвращает вызов функции createPrint(). Этот вызов возвращает функцию, так что теперь myPrint - это функция. Вызовите её, и на экран выведется "King".</p>
45
<p>Тут есть странная штука: эта константа name была создана внутри функции createPrint. Функция была вызвана и исполнена. Как мы знаем, когда функция заканчивает работу, она больше не существует. Этот магический ящик исчезает со всеми своими внутренностями.</p>
45
<p>Тут есть странная штука: эта константа name была создана внутри функции createPrint. Функция была вызвана и исполнена. Как мы знаем, когда функция заканчивает работу, она больше не существует. Этот магический ящик исчезает со всеми своими внутренностями.</p>
46
<p>НО он возвратил другую функцию, и уже она как-то запомнила константу name. Поэтому когда мы вызывали myPrint(), она вывела "King" - запомненное значение, при том, что область видимости, где оно было задано больше не существует.</p>
46
<p>НО он возвратил другую функцию, и уже она как-то запомнила константу name. Поэтому когда мы вызывали myPrint(), она вывела "King" - запомненное значение, при том, что область видимости, где оно было задано больше не существует.</p>
47
<p>Функция, которая была возвращена из createPrint, называется замыканием. Замыкание - это сочетание функции и окружения, где она была задана. Функция "замкнула" в себе некоторую информацию из области видимости.</p>
47
<p>Функция, которая была возвращена из createPrint, называется замыканием. Замыкание - это сочетание функции и окружения, где она была задана. Функция "замкнула" в себе некоторую информацию из области видимости.</p>
48
<p>Это может выглядеть как JavaScript-фокус, но замыкания, когда их используют разумно, могут сделать код приятней, чище и проще для чтения. И сама идея возврата функций тем же способом, которым можно возвращать числа и строки, даёт больше возможностей и гибкости.</p>
48
<p>Это может выглядеть как JavaScript-фокус, но замыкания, когда их используют разумно, могут сделать код приятней, чище и проще для чтения. И сама идея возврата функций тем же способом, которым можно возвращать числа и строки, даёт больше возможностей и гибкости.</p>
49
<p>Вы заметите, как часто эти идеи используются в программировании, и мы рассмотрим их мощь в следующих курсах.</p>
49
<p>Вы заметите, как часто эти идеи используются в программировании, и мы рассмотрим их мощь в следующих курсах.</p>
50
<h2>Выводы</h2>
50
<h2>Выводы</h2>
51
<ul><li><strong>Область видимости</strong>(scope) компонентов - это местоположение, где эти компоненты доступны.</li>
51
<ul><li><strong>Область видимости</strong>(scope) компонентов - это местоположение, где эти компоненты доступны.</li>
52
<li>Компоненты, созданные снаружи функций, инструкций с if, циклов и так далее, находятся в<strong>глобальной области видимости</strong></li>
52
<li>Компоненты, созданные снаружи функций, инструкций с if, циклов и так далее, находятся в<strong>глобальной области видимости</strong></li>
53
<li>Фигурные скобки { } задают новую<strong>локальную область видимости</strong></li>
53
<li>Фигурные скобки { } задают новую<strong>локальную область видимости</strong></li>
54
</ul><h3>Глобальная против локальной</h3>
54
</ul><h3>Глобальная против локальной</h3>
55
<p>Локальные константы и переменные не видимы снаружи их области видимости:</p>
55
<p>Локальные константы и переменные не видимы снаружи их области видимости:</p>
56
<p>Но если x представлен глобально, то он доступен:</p>
56
<p>Но если x представлен глобально, то он доступен:</p>
57
<p>Возможен доступ к внешней области видимости:</p>
57
<p>Возможен доступ к внешней области видимости:</p>
58
<p>Функция фактически производит что-то, только когда она вызывается, а не задаётся, поэтому изначально a это 0, но после того, как вызвана changer, a становится 1.</p>
58
<p>Функция фактически производит что-то, только когда она вызывается, а не задаётся, поэтому изначально a это 0, но после того, как вызвана changer, a становится 1.</p>
59
<h3>Лексическая область видимости</h3>
59
<h3>Лексическая область видимости</h3>
60
<p>JavaScript пытается найти значение в текущем окружении. Но значение не находится и JavaScript выходит наружу, на один уровень за попытку, пока не найдёт значение или не поймет, что значение невозможно найти.</p>
60
<p>JavaScript пытается найти значение в текущем окружении. Но значение не находится и JavaScript выходит наружу, на один уровень за попытку, пока не найдёт значение или не поймет, что значение невозможно найти.</p>
61
<p>Здесь, в выражении a * b , функция multiplier использует локальную a (потому что она обнаружена локально), и наружную b (потому что локально b найдена не была).</p>
61
<p>Здесь, в выражении a * b , функция multiplier использует локальную a (потому что она обнаружена локально), и наружную b (потому что локально b найдена не была).</p>
62
<h3>Замыкания</h3>
62
<h3>Замыкания</h3>
63
<p>myPrint - это функция, которая была возвращена createPrint. Несмотря на то, что вызов createPrint окончен и константы name больше не существует, значение запомнено в myPrint.</p>
63
<p>myPrint - это функция, которая была возвращена createPrint. Несмотря на то, что вызов createPrint окончен и константы name больше не существует, значение запомнено в myPrint.</p>
64
<p>Это<strong>замыкание</strong>: сочетание функции и окружения, где она была заявлена.</p>
64
<p>Это<strong>замыкание</strong>: сочетание функции и окружения, где она была заявлена.</p>