Область видимости
1. Область видимости
Область видимости (scope) — это независимая от языка концепция, которая описывает доступность переменных в исполняемом коде.
Scope chain (цепочка областей видимости) - области видимости образуют иерархию, так что дочерние области имеют доступ к переменным из родительских областей, но не наоборот.
Переменная видна для исполняемого кода, если она есть в текущей области видимости или в цепочке областей видимости функции.
Есть три типа областей видимости:
Переменные, объявленные на самом верхнем уровне, то есть вне любых конструкций вроде
if
,while
,for
и функций, находятся в глобальной области видимости.Переменные, объявленные внутри инструкций
if
, циклов и других блоков кода находятся в блочной области видимости.Переменные, объявленные внутри функций, находятся в области видимости функции.
Это можно представить как дом с комнатами. Дом находится в глобальной области видимости. Каждая функция и блок создают новую комнату, вложенную внутрь дома. Переменные, объявленные внутри этих комнат, доступны только тогда, когда вы находитесь внутри этой комнаты. Вне комнаты эти переменные недоступны.
// Global scope
function foo() {
// Local function scope of foo
if (true) {
// Local block scope
}
// Local function scope of foo
function bar() {
// Local function scope of bar
}
// Local function scope of foo
}
// Global scope
Copy
Интерпретатор пытается сначала найти переменную в той области видимости, в которой к ней обратились. Если такой переменной в локальной области видимости нет, то он выходит наружу, на один уровень за попытку, пока не найдёт значение или не дойдет до самой верхней области видимости (глобальной) и поймет, что переменную с таким идентификатором невозможно найти, так как ее просто нет, тогда будет ошибка о том, что переменная не объявлена.
Функция add
возвращает сумму a
и b
. Переменная a
объявлена внутри функции, b
нет.
const b = 10;
const add = function () {
const a = 5;
return a + b;
};
add(); // 15
Copy
Пытаясь решить выражение a + b
, интерпретатор ищет значения a
и b
. Поиск начинается внутри локальной области видимости — внутри функции add
. Он находит значение a
и переходит к b
. Невозможно найти значение b
в локальной области видимости, поэтому поиск расширяется до наружной области. Тут он находит b
— это 10
. Выражение a + b
превращается в 5 + 10
, в результате получаем 15
.
2. Область видимости функции
Как уже говорилось, функции создают собственную локальную область видимости. Переменные, созданные внутри функции, включая параметры, локальны внутри этой функции и не доступны коду из вне. Локальные переменные будут создаваться каждый раз при вызове функции, и их отдельные инкарнации никак друг с другом не связаны.
Глобальный value
объявлен вне тела функции и его значение будет выведено в консоль. Локальный value
внутри функции add
по прежнему виден только внутри этой функции. Эти два value
не имеют ничего общего друг с другом, они находятся в разных областях видимости. Они не схлопываются в одно целое, не смотря на то, что у них одно и то же имя. В свою очередь innerValue
не доступна вне тела функции.
const value = 50;
const add = function (num) {
const value = 10;
const innerValue = 5;
return num + value + innerValue;
};
// value объявлен глобально и поэтому доступен
console.log(value); // 50
console.log(add(20)); // 35
// Ошибка, переменная innerValue не объявлена в этой области
// видимости, она доступна только внутри функции add
console.log(innerValue);
Copy
Такое поведение помогает предотвратить случайное взаимодействие между функциями. Если бы все переменные использовались в любом месте программы, было бы очень трудно убедиться, что одна переменная не используется для разных операций.
Рассматривая локальные для функции переменные как существующие только внутри функции, язык делает возможным работу с функциями как с изолированными контейнерами, что позволяет не волноваться про весь код целиком.
3. Hoisting
В языках программирования, в том числе в JavaScript, код исполняется в две фазы.
Фаза компиляции, интерпретации или оценки (compile time, evaluation time) - подготовка перед исполнением кода, проверка валидности синтаксиса исходного кода.
Во время этой фазы компилятор или интерпретатор находит синтаксические ошибки, ошибки типизации и т. д. То есть код еще не выполняется, только оценивается. Если эта фаза прошла успешно, это как минимум значит, что в коде нет синтаксических ошибок и его можно запустить на исполнение.
Фаза исполнения (runtime) - скрипт начинает исполняться, выполняются инструкции вызовов функций и оценки выражений, происходит поиск необходимых идентификаторов в соответствующих областях видимости и тому подобное.
Если эта фаза завершилась успешно, значит скрипт написан без явных ошибок и закончил свою работу. На этой фазе могут быть ошибки, связанные с отсутствующими свойствами и переменными, преобразованием типов и т. д., то есть что-то, что происходит только во время выполнения кода.
Попробуйте выполнить следующий код. Так как мы сделали опечатку и вместо const
пытаемся объявить переменную value
ключевым словом cos
, на фазе компиляции будет выявлена синтаксическая ошибка и фаза исполнения даже не запустится. В консоли мы сразу увидим сообщение об ошибке.
console.log('Этого сообщения не будет в консоли.');
cos value = 5;
Copy
Поднятие переменных (hoisting) - это механизм интерпретатора, который, до фазы исполнения кода, поднимает объявления переменных в начало области видимости (блочной или функции) в которой они были объявлены.
Именно поэтому работает function declaration
и так странно ведут себя переменные, объявленные используя var
- их объявления поднимаются в начало области видимости функции в которой они были объявлены.
// Вот поэтому мы используем let или const
console.log(value); // undefined
value = 5;
if (true) {
console.log(value); // 5
var value = 10;
console.log(value); // 10
}
value = 15;
console.log(value); // 15
Copy
Переменные, объявленные используя let
или const
так же поднимаются, но при этом подчиняются блочной области видимости, ничем не инициализируются по умолчанию и не доступны для обращения до того места в коде, где были объявлены в коде.
// В каждой области видимости будет создана своя, независимая переменная value
console.log(value); // ReferenceError: value is not defined
const value = 5;
console.log(value); // 5
if (true) {
console.log(value); // ReferenceError: value is not defined
const value = 10;
console.log(value); // 10
}
console.log(value); // 5
Copy
Более детально про поднятие идентификаторов и ключевые словах var
, let
и const
читайте в этой статье.
Last updated