Область видимости переменных

Последнее обновление: 23.08.2023

Все переменные и константы в JavaScript имеют определенную область видимости, в пределах которой они могут действовать.

Глобальные переменные

Все переменные и константы, которые объявлены вне функций, являются глобальными:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
	<title>METANIT.COM</title>
</head>
<body>
<script>
var a = 5;
let b = 8;
const c = 9;
function displaySum(){
 
    var d = a + b + c;
    console.log(d);
}
displaySum(); // 22
</script>
</body>
</html>

Здесь переменные a и b и константа c являются глобальными. Они доступны из любого места программы.

А вот переменная d глобальной не является, так как она определена внутри функции и видна только в этой функции.

Определение локальной области видимости

Для определения локальной области видимости в JavaScript используются фигурные скобки { }, которые создают блок кода. Этот блок кода может быть безымянным, может быть именнованным, например, функция, либо может представлять условную или циклическую конструкцию. Например, определение переменных в безымянном блоке кода:

{
	var a = 5;
	let b = 8;
	const c = 9;
}

Однако в этом случае поведение переменной зависит от способа ее определения (через var или через let) и от типа блока. var определяет локальные переменные уровня функции, а let определяет локальные переменные уровня блока кода (подобным образом const определяет константы уровня блока кода). Рассмотрим, в чем состоит отличие.

Переменные и константы функции

Переменные и константы, определенные внутри функции, видны (то есть могут использоваться) только внутри этой функции:

function print(){

	var a = 5;
	let b = 8;
	const c = 9;
	console.log("Function print: a =", a);
	console.log("Function print: b =", b);
	console.log("Function print: c =", c);
}
print();
console.log("Global: a =", a);	// Uncaught ReferenceError: a is not defined

Переменные a и b и константа c являются локальными, они существуют только в пределах функции. Вне функции их нельзя использовать, поэтому мы получим следующий консольный вывод:

Function print: a= 5
Function print: b= 8
Function print: c= 9
Uncaught ReferenceError: a is not defined

Здесь мы видим, что при попытке обратиться к переменной a вне функции print(), браузер выводит ошибку. При этом подобное поведение не зависит от того, что это за переменная - var или let, либо это константа. Подобное поведение для всех переменных и констант одинаково.

Локальные переменные в блоках кода, условиях и циклах

С переменными, которые определяются в безымянных блоках кода, а также в циклах и условных конструкциях ситуация чуть сложнее.

Переменная var

Переменная, объявленная с помощью var, может использоваться вне блока:

// безымянный блок
{
	var a = 5;
}
console.log("a =", a);	// a = 5

// условная конструкция
if(true){
	var b = 6;
}
console.log("b =", b);	// b = 6

// цикл
for(let i = 0; i < 5; i++){
	var c = 7;
}
console.log("c =", c);	// c = 7

Единственное условие, что блок кода должен срабатывать, чтобы инициализировать переменную. Так, в примере выше условие в конструкции if и в цикле for установлено так, что блок этих конструкций будет выполняться. Однако, что если условие будет иным, и блок не будет выполняться?

if(false){
	var b = 6;
}
console.log("b =", b);	// b = undefined

// цикл
for(let i = 1; i < 0; i++){
	var c = 7;
}
console.log("c =", c);	// c = undefined

В таком случае мы опять же сможем обращаться к переменным, только они будут иметь значение undefined.

Переменная let и константы

Теперь посмотрим, как будут вести себя в подобной ситуации переменные, определенные с помощью let:

{
	let a = 5;
}
console.log("a =", a);	// Uncaught ReferenceError: a is not defined

В данном случае мы получим ошибку. Мы можем использовать переменные let, определенные внутри блока кода, только внутри этого блока кода.

Тоже самое относится и к константам:

{
	const b = 5;
}
console.log("b =", b);	// Uncaught ReferenceError: b is not defined

Скрытие переменных

Что если у нас есть две переменных - одна глобальная, а другая локальная, которые имеют одинаковое имя:

var z = 89;
function print(){

	var z = 10;
	console.log(z); // 10
}
print(); // 10

В этом случае в функции будет использоваться та переменная z, которая определена непосредственно в функции. То есть локальная переменная скроет глобальную. Однако конкретное поведение при скрытии зависит от того, как определяется переменная.

Скрытие переменной var

Выше было указано, что var определяет переменную уровня функции. Поэтому с помощью оператора var мы НЕ можем определить одновременно две переменных с одним и тем же именем и в функции, и в блоке кода в этой функции. Если мы это сделаем, то при объявлении переменной на уровне блока мы меняем значение переменной уровня функции:

function displayZ(){
 
    var z = 20;
	
	{
		var z = 30;	// Не определяет новую переменную, а изменяет значение переменной z уровня функции
		console.log("Block:", z);
	}
	console.log("Function:", z);
}
displayZ();

Здесь определение переменной z внутри блока фактически будет равноценно изменению значения переменной уровня функции и фактически это будет одна и та же переменная. Консольный вывод:

Block: 30
Function: 30

Скрытие переменной let

Как писалось выше, оператор let определяет переменную уровня блока кода. То есть каждый блок кода определяет новую область видимости, в которой существует переменная. Вне блока кода, где определена переменная, она не существует. Соответственно мы можем одновременно определить переменную на уровне блока и на уровне функции (в отличие от var):

let z = 10;
function displayZ(){
 
    let z = 20;
	
	{
		let z = 30;
		console.log("Block:", z);
	}
	
	console.log("Function:", z);
}

displayZ();
console.log("Global:", z);

Здесь внутри функции displayZ определен блок кода, в котором определена переменная z (вместо безымянного блока это мог быть и блок условной конструкции или цикла). Она скрывает глобальную переменную и переменную z, определенную на уровне функции.

И в данном случае мы получим следующий консольный вывод:

Block: 30
Function: 20
Global: 10

Константы

Все, что относится к оператору let, относится и к оператору const, который определяет константы уровня блока кода. Блоки кода задают область видимости констант, а константы, определенные на вложенных блоках кода, скрывают внешние константы с тем же именем:

const z = 10;
function displayZ(){
 
    const z = 20;
	
	{
		const z = 30;
		console.log("Block:", z);	// 30
	}
	console.log("Function:", z);	// 20
}

displayZ();
console.log("Global:", z);	// 10

Scope chain / Цепочка областей видимости

При выполнении кода, когда интерпретатор сталкивается с каким-то идентификатором (название переменной, константы, функции), то вначале он ищет опеределение этого идентификатора в текущей области видимости, благодаря чему собственно и работает скрытие переменных и констант. Например,

const z = 10;
function displayZ(){
 
    const z = 20;
	console.log(z);	// 20
}

displayZ();     // 20

Здесь интепретатор увидит, что в функции displayZ идет обращение к идентификатору z, и будет искать определение этого идентификатора внутри функции displayZ. И поскольку в этой функции есть определение константы const z = 20, то именно эта константа и будет использоваться.

Другой пример:

const z = 10;
function displayZ(){
 
	console.log(z);	// 10
}

displayZ();     // 10

Теперь внутри функции displayZ нет определения идентификатора z, поэтому для его поиска применяется scope chain - интерпретатор обращается к окружающей области видимости и выполняет поиск там. То есть смотри области видимости по цепочке от текущей - к внешним вплоть до глобальной области видимости.

Необъявленные переменные

При определении переменных в JavaScript мы можем не использовать ключевое слово let или var. Например:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>METANIT.COM</title>
</head>
<body>
    <script>
        {
            username = "Tom"; 
        }
        console.log(username);  // ошибки нет
		{
            console.log(username);  // ошибки нет, доступна внутри других блоков кода
        }
    </script>
</body>
</html>

Если мы не используем это ключевое слово let/var при определении переменной в функции, то такая переменная будет глобальной. Например::

function setAge(){
    userage = 39;
}
setAge();
console.log(userage);   // 39

Несмотря на то, что вне функции setAge переменная userage нигде не определяется, тем не менее она доступна вне функции во внешнем контексте. Единственное условие - мы вызываем функцию, где определена такая переменная..

Однако если мы не вызовем функцию, переменная будет не определена:

function setAge(){
	userage = 39;
}

// setAge();	Функция НЕ вызывается
console.log(userage);	// ошибка - Uncaught ReferenceError: userage is not defined

Ту же ошибку мы получили бы, если бы мы не только присваивали значение переменной, но и определяли бы ее как локальную по отношению к функции:

function setAge(){
	var userage = 39;
}

setAge();
console.log(userage);	// ошибка - Uncaught ReferenceError: userage is not defined

strict mode

Определение глобальных переменных в функциях может вести к потенциальным ошибкам. Чтобы их избежать используется строгий режим или strict mode. Установить режим strict mode можно двумя способами:

  • добавить выражение "use strict" в начало кода JavaScript, тогда strict mode будет применяться для всего кода

  • добавить выражение "use strict" в начало тела функции, тогда strict mode будет применяться только для этой функции.

Глобальное применение strict mode:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>METANIT.COM</title>
</head>
<body>
    <script>
        "use strict";       // используем строгий режим

        username = "Tom";   // Uncaught ReferenceError: username is not defined
        console.log(username);
    </script>
</body>
</html>

В этом случае мы получим ошибку SyntaxError: Unexpected identifier, которая говорит о том, что переменная username не определена.

Аналогичную ошибку мы получим при определении глобальной переменной в функции:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>METANIT.COM</title>
</head>
<body>
    <script>
        "use strict";       // используем строгий режим
        function setAge(){
            userage = 39;		// Uncaught ReferenceError: userage is not defined
        }
        setAge();
        console.log(userage);
    </script>
</body>
</html>

Пример использования строгого режима на уровне функции:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>METANIT.COM</title>
</head>
<body>
    <script>
        username = "Tom";   // норм
        console.log(username);  // Tom

        function setAge(){
            "use strict";       // используем строгий режим на уровне функции
            userage = 39;		// Uncaught ReferenceError: userage is not defined
        }
        setAge();
        console.log(userage);
    </script>
</body>
</html>
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850