Объяснение Scope (Chain) в JavaScript с помощью визуализации
Это перевод статьи Лидии Холли, где она разбирает такое понятие как Scope (Chain) с помощью визуализации.
Давайте рассмотрим следующий код:
const name = "Lydia"
const age = 21
const city = "San Francisco"
function getPersonInfo() {
const name = "Sarah"
const age = 22
return `${name} is ${age} and lives in ${city}`
}
console.log(getPersonInfo())
В коде мы вызываем функцию getPersonInfo, которая возвращает строку, содержащую значения переменных name, age и city:
Sarah is 22 and lives in San Francisco. Но функция getPersonInfo не содержит переменную city 🤨 Как функция смогла получить значение переменной city?
Во-первых, пространство памяти настраивается для разных контекстов. По умолчанию global context(window в браузере, global в ноде) и local context для вызванной функции getPersonInfo. У каждого контекста также есть цепочка областей видимости (scope chain).
Для функции getPersonInfo цепочка областей видимости (scope chain) выглядит так:

Scope chain в основном представляет собой «цепочку ссылок» на объекты, которые содержат ссылки на значения и другие скоупы, на которые можно ссылаться в этом контексте выполнения. Scope chain создается при создании контекста выполнения, то есть создается в runtime.
Однако давайте сосредоточимся на scope. В следующих примерах пары key/value в контекстах выполнения представляют ссылки, которые в scope как переменные.

Scope chain глобального контекста выполнения содержит ссылку на 3 переменные: name с значением Lydia, age с значением 21, и city с значением San Francisco. В local context ссылки на 2 переменные: name с значением Sarah и age с значением 22.
Пытаясь получить доступ к переменным в функции getPersonInfo, движок сначала проверяет цепочку локального скоупа.

В локальном scope chain содержиться ссылка на name и age. У переменной name значение Sarah и у переменной age значение 22. Но что происходит когда мы пытаемся доступиться до city?
Для того чтобы найти значение переменной city, движок «спускается вниз по цепочке». В этом кейсе он ищет переменную города в внешнем скоупе, на который ссылается локальный скоуп.

Мы можем спуститься по scope chain, но не можем подняться вверх по цепочке. (Хорошо, это может сбивать с толку, потому что некоторые люди говорят «вверх», а не «вниз», поэтому я просто перефразирую: вы можете перейти к внешним scope, но не к "более внутренним скоупам". Это можно визуализировать как водопад:

Или даже глубже:

Возьмем этот код в качестве примера.

Это почти то же самое, но есть одно большое отличие: теперь мы объявили city только в функции getPersonInfo, а не в global scope. Мы не вызывали функцию getPersonInfo, поэтому локальный контекст тоже не создается. Теперь давайте запросим доступ к значениям имени, возраста и города в глобальном контексте.

Эта попытка выдаст ошибку ReferenceError! Не удалось найти ссылку на переменную с именем city в глобальной области видимости, и не было других скоупов для поиска, и она не может подняться по цепочке областей видимости.
Таким образом, можно использовать область видимости как способ «защитить» свои переменные и повторно использовать имена переменных.
Помимо глобальной и локальной областей, существует также block scope. Переменные, объявленные с помощью ключевого слова let или const, ограничены ближайшими фигурными скобками ({}).
const age = 21
function checkAge() {
if (age < 21) {
const message = "You cannot drink!"
return message
} else {
const message = "You can drink!"
return message
}
}
Это можно визуализировать следующим образом:

Как видите, есть глобальная область видимости (синим), область видимости функции (оранжевым) и две области видимости блока (желтым). Мы смогли объявить переменную дважды, так как переменные созданы внутри разных скоупов (в фигурных скобках).
Итог:
- Вы можете рассматривать "scope chain" как цепочку ссылок на значения, к которым мы можем получить доступ в текущем контексте.
- Scopes также позволяют повторно использовать имена переменных, которые определены ниже по scope chain, поскольку она может идти только вниз по цепочке областей видимости, но не вверх.