Объяснение 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, поскольку она может идти только вниз по цепочке областей видимости, но не вверх.