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

Области видимости и замыкания в JavaScript

Илья Ушаков
Илья Ушаков
Технический писатель
31 марта 2023 г.
851
9 минут чтения
Средний рейтинг статьи: 5

JavaScript — это язык программирования, который широко используется в веб-разработке. Он известен своей способностью динамически изменять и обрабатывать содержимое веб-страницы. Одним из важных аспектов языка JavaScript являются области видимости и замыкания, которые позволяют программистам эффективно управлять разными элементами в коде программы.

Область видимости: глобальная и локальная

Область видимости — зона доступности элементов в коде. Она может быть глобальной или локальной.

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

В качестве примера приведем фрагмент кода ниже:

let breed = "Doberman";

function BreedOfDog() {
  console.log(`Breed of dog: ${breed}.`);
}

BreedOfDog();

В примере выше breed — глобальная переменная. Она может быть использована в любой части программы, включая функцию BreedOfDog(). При вызове последней, используется breed, чтобы вывести название породы собаки. 

Однако использование глобальных переменных может привести к проблемам. Рассмотрим пример ниже:

let breed = "Doberman";

function BreedOfDog() {
  console.log(`Breed of dog: ${breed}.`);
}

function changeTheBreed() {
  breed = "Labrador";
}

BreedOfDog();
changeTheBreed();
BreedOfDog();

Здесь changeTheBreed() изменяет значение breed, и как результат, BreedOfDog() выводит другую породу собаки со значением breed, равным «Labrador». Такое поведение может вызвать путаницу в коде и усложнить процесс отслеживания использования переменных.

Локальная область видимости означает, что элементы доступны только в определенном блоке или функции. Данный факт поможет избежать конфликтов между элементами, объявленными в разных частях программы. Из определения выше следует, что локальную область видимости можно разбить на два типа: функциональная и блочная.

Функциональная область видимости

Функциональная область видимости — это зона видимости элементов, объявленных внутри функции. 

В качестве примера представим фрагмент кода ниже:

function multiplication(z, x) {
  var res = z * x;
  return res;
}

console.log(multiplication(4, 5)); // 20
console.log(res); // Ошибка

Здесь res заявлена внутри multiplication(), поэтому она будет доступна только в рамках этой функции. Если попробовать обратиться к res за ее пределами, JavaScript вернет ошибку ReferenceError.

Блочная область видимости

Блочная область видимости — это зона видимости переменных, объявленных внутри блока кода. Это может быть любой блок кода, который заключен в фигурные скобки, например, условный оператор if, либо цикл for

В качестве примера представим код ниже:

function calculatePrice(quantity) {
  let price = 100;
  if (quantity > 10) {
    let discount = 0.1;
    price = price - (price * discount);
  }
  console.log("Total price: " + price);
  console.log("Discount: " + discount);
}

calculatePrice(15);

Здесь мы определяем функцию calculatePrice(). Она принимает количество товаров в качестве аргумента. Внутри нее определяются две переменные: price и discount. price инициализируется значением 100.

Затем мы используем конструкцию блока if, чтобы проверить, является ли количество товаров больше 10. Если это так, мы создаем новую переменную discount и устанавливаем ее значение равным 0.1, что означает 10% скидку. Затем мы изменяем значение price, чтобы учесть эту скидку.

Наконец, мы выводим общую стоимость покупки и скидку на консоль. Однако, если мы попробуем обратиться к discount за пределами конструкции if, как в нашем примере, мы получим ошибку.

Image5

Поднятие функций

Подъем функции (hoisting) — это механизм, который позволяет функции быть доступной до того, как она была объявлена в коде. Однако, только объявления функций поднимаются, а не их определения.

Рассмотрим пример:

let breed = "Doberman";

BreedOfDog();

function BreedOfDog() {
  console.log(`Breed of dog: ${breed}.`);
}

В данном случае BreedOfDog() будет поднята вверх до вызова, поэтому код будет работать без ошибок.

Image6

Кроме того, возможно объявить переменную и присвоить ей функцию как выражение. Тогда подъем не будет происходить.

let breed = "Doberman";

BreedOfDog();  // TypeError: BreedOfDog is not a function

var BreedOfDog = function() {
  console.log(`Breed of dog: ${breed}.`);
}

Код выше не будет работать и приведет к ошибкам из-за того, что функция вызывается до ее инициализации.

Image7

Ограничение областей видимости разных функций

Любая функция имеет свою уникальную область видимости. Поэтому она не имеет доступа к элементам, объявленным в другой функции, если они не были переданы в качестве аргументов.

Приведем пример ниже:

example2();

function example1() {
var var1 = "Secret message for example1";
}

function example2() {
var var2 = "Secret message for example2";
console.log(var2);
console.log(var1); 
}

В примере выше var1 не доступна для example2(). Поэтому при вызове console.log(var1) появится ошибка.

Image2

Вложенные области видимости

Вложенные области видимости получаются в тех случаях, когда одна из них находится внутри другой. Это означает, что внутренняя область может получить доступ к элементам, объявленным внешней областью видимости. Наоборот данное правило не работает.

Для наглядности можно изучить пример ниже:

example1();

function example1() {
var var1 = "Secret message for example1";

function example2() {
var var2 = "Secret message for example2";
console.log(var1); 
}
example2(); // Secret message for example1
console.log(var2); // ReferenceError: var2 is not defined
}

Здесь функции example2() доступна переменная var1, которая определена для example1(). Однако, example1() не имеет доступа к var2, которая определена для example2(). При попытке обратиться к var2 из example1() будет вызвана ошибка ReferenceError. Это происходит из-за того, что var2 находится в области видимости example2(), и к ней нельзя обратиться из элементов кода.

Замыкания

Замыкание — это механизм работы с областями видимости в JavaScript, который позволяет сохранять доступ к переменным даже после завершения работы функции, в которой эти переменные были объявлены.

Пример:

function breed() {
  var nameOfBreed = "Doberman";
  return function BreedOfDog() {
    console.log(`Breed of dog: ${nameOfBreed}.`);
  }
}

var dog = breed();
dog(); 

В примере выше breed() возвращает BreedOfDog(), которая запоминает значение переменной nameOfBreed в момент ее определения. Затем передаем вызов breed() в переменную dog. После этого мы вызываем функцию dog(), и она, используя сохраненное значение nameOfBreed, выводит строку «Breed of dog: Doberman».

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

Контроль побочных эффектов

Побочный эффект — это изменение состояния программы вне функции. Например, когда функция изменяет значение переменной вне ее области видимости, это будет побочным эффектом. Контроль побочных эффектов подразумевает, что функции не должны изменять состояние программы вне своей области видимости. Вместо этого они должны возвращать значения, которые могут понадобиться в других частях программы.

Пример:

function createCounter() {
  let count = 1;

  function increment() {
    count *= 2;
    return count;
  }

  return increment;
}

const counter = createCounter();

console.log(counter()); // 2
console.log(counter()); // 4
console.log(counter()); // 8

В примере выше createCounter() возвращает вложенную функцию increment(), которая изменяет count внутри своей области видимости. После того, как createCounter() была вызвана, создается замыкание, которое сохраняет count в памяти и возвращает ссылку на increment(). Таким образом, во время вызова counter(), изменяется значение count и возвращается новое значение. 

Приватные переменные

Замыкания также используются в целях создания приватных переменных и методов. Приватные переменные — это переменные, которые доступны только внутри функции. Это может быть полезно, когда нужно скрыть какую-то информацию или защитить ее от изменений.

В примере из прошлого раздела была объявлена переменная count, которая является приватной, так как она недоступна извне и не может изменяться напрямую.

Проверка областей видимости с помощью DevTools

В процессе разработки JavaScript-приложений, возможно, вы столкнетесь с проблемами связанными с областями видимости. Одним из инструментов, который поможет вам легко отслеживать и отлаживать проблемы с ними, является DevTools.

Ниже перечислим способы применения DevTools:

  • Breakpoints

Breakpoints — это места в коде, на которых выполнение скрипта будет остановлено, чтобы вы могли проанализировать текущее состояние области видимости.

Чтобы установить точку останова, вам нужно щелкнуть на номере строки в редакторе кода в DevTools. После установки точки останова вы можете запустить код и выполнение скрипта остановится на заданной строке.

Image4

Как видно по рисунку выше, breakpoints были успешно установлены. На это указывают синяя подсветка номера строки кода и список точек останова в правом меню «Breakpoints».

  • Ключевое слово «Debugger»

Ключевое слово debugger — это инструкция, которая при выполнении включает отладчик JavaScript в браузере.

Чтобы использовать debugger, вы должны вставить его в код:

function greet(name) {
  let greeting = "Hello";
  console.log(`${greeting}, ${name}!`);
  debugger;
  console.log("Done!");
}

greet("John");

Когда браузер выполняет этот код и достигает инструкции debugger, он останавливается, и DevTools автоматически открывается на вкладке «Sources» с текущим местоположением курсора, как показано на рисунке ниже.

Image1

  • Watch expressions

Watch expressions — это выражения, которые вы можете добавить в DevTools, чтобы отслеживать значения переменных в режиме реального времени.

Чтобы добавить выражение, вы можете щелкнуть на кнопке «Add Watch Expression» в панели Sources в DevTools и ввести выражение, которое вы хотите отслеживать.

Например, если вы хотите отслеживать значение переменной name в режиме реального времени, вы можете добавить следующее выражение:

Image3

Это позволит вам отслеживать в режиме реального времени изменения значений переменных.

Заключение

В данной статье было рассказано про области видимости и замыкания в JavaScript. При правильном подходе, они позволяют программистам создавать безопасный и управляемый код, который можно легко читать и поддерживать. Также, мы рассказали про возможные способы применения DevTools, которые помогут отслеживать и отлаживать проблемы связанными с областями видимости.

Зарегистрируйтесь и начните пользоваться
сервисами Timeweb Cloud прямо сейчас

15 лет опыта
Сосредоточьтесь на своей работе: об остальном позаботимся мы
165 000 клиентов
Нам доверяют частные лица и компании, от небольших фирм до корпораций
Поддержка 24/7
100+ специалистов поддержки, готовых помочь в чате, тикете и по телефону