HTML Diff
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>