Оригінальна публікація: Lexical Scope in JavaScript – What Exactly Is Scope in JS?

Термін «лексична область» на перший погляд може здатися неочевидним. Корисно було б розібрати значення цих слів окремо.

Ця стаття пояснить лексичну область, спершу розібравши значення слів «лексична» та «область».

Давайте почнемо з розбору значення слова «область».

Що ж таке область?

Область — це простір, в якому предмет (функція або змінна) є видимим та доступним для решти коду.

Зверніть увагу:

  • Область — це територія, простір або регіон.
  • Глобальна область — це глобальний простір (або публічний).
  • Локальна область — це місцевий простір (або обмежений).

Ось приклад:

// Визначення змінної в глобальній області:
const fullName = "Oluwatobi Sofela";

// Визначення вкладених функцій:
function profile() {
  function sayName() {
    function writeName() {
      return fullName;
    }
    return writeName();
  }
  return sayName();
}

Спробуйте на StackBlitz

У фрагменті вище ми визначили змінну fullName в глобальній області. Через це вона є видимою та доступною глобально всьому коду всередині цього скрипту.

Але ми визначили writeName() всередині функції sayName(), тому writeName() перебуває в локальній області sayName().

Не забувайте, що кожен раз, коли ми викликаємо функцію writeName(), комп’ютер не піде до глобальної області, щоб викликати змінну fullName. Натомість, він послідовно пройде через ланцюг областей, шукаючи fullName.

Що таке ланцюг областей?

Ланцюгом областей називають унікальні простори, які існують поза областю, де змінну викликали до глобальної області.

Для прикладу:

// Визначення змінної в глобальній області:
const fullName = "Oluwatobi Sofela";

// Визначення вкладених функцій:
function profile() {
  function sayName() {
    function writeName() {
      return fullName;
    }
    return writeName();
  }
  return sayName();
}

Зверніть увагу, в фрагменті вище змінна fullName була викликана з області функції writeName().

Тому ланцюг областей, який існує від виклику змінної до глобальної області, виглядатиме так:

Область writeName() ---> область sayName() ---> область profile() ---> глобальна область

Іншими словами, від області виклику змінної fullName до її лексичної області (в даному випадку глобальної області) знаходяться чотири інших областей.

Зверніть увагу: глобальна область є останньою ланкою в ланцюгу областей JavaScript.

Як працює ланцюг областей?

Ланцюг областей JavaScript визначає ієрархію областей, через які має пройти комп’ютер, щоб знайти лексичну область конкретної функції, яка була викликана.

Для прикладу розглянемо такий код:

// Визначення змінної в глобальній області:
const fullName = "Oluwatobi Sofela";

// Визначення вкладених функцій:
function profile() {
  function sayName() {
    function writeName() {
      return fullName;
    }
    return writeName();
  }
  return sayName();
}

У фрагменті вище, кожен раз при виклику функції profile(), комп’ютер спочатку викликатиме функцію sayName(), яка є єдиним кодом всередині функції profile().

Після цього комп’ютер викликатиме функцію writeName(), яка є єдиним кодом всередині функції sayName().

Тепер, оскільки код всередині writeName() каже комп’ютеру викликати та повернути вміст змінної fullName, комп’ютер викличе fullName. Але комп'ютер не піде напряму до глобальної області, щоб викликати fullName.

Натомість комп’ютер крок за кроком проходить через ланцюг областей, щоб знайти лексичну область змінної fullName.

Ось послідовність кроків, які має зробити комп’ютер, щоб знайти лексичну область змінної fullName:

  1. Комп’ютер провірить, чи змінна fullName була визначена локально всередені функції writeName(). Але він не знайде визначення fullName тут, тому він рухається до наступної області, щоб продовжити свої пошуки.
  2. Комп’ютер шукатиме визначення fullName всередині sayName() (це наступна область в ланцюгу). Він не знайде визначення змінних тут, тому далі рухається вверх по ланцюгу.
  3. Комп’ютер шукатиме визначення fullName у функції profile(). Визначення fullName тут немає. Комп’ютер далі шукає лексичну область змінної fullName в наступній частині ланцюга областей.
  4. Комп’ютер доходить до глобальної області (наступна область після profile()). На щастя, він знаходить визначення fullName! Як наслідок, комп’ютер отримає вміст змінної ("Oluwatobi Sofela") і поверне його.

Час попрактикуватися з областю 🤸‍♂️🏋️‍♀️🏊‍♀️

Розглянемо код. Яку з трьох змінних fullName викличе комп’ютер?

// Спочатку змінна fullName визначається в глобальній області:
const fullName = "Oluwatobi Sofela";

// Вкладені функції, які містять ще дві змінні fullName:
function profile() {
  const fullName = "Tobi Sho";
  function sayName() {
    const fullName = "Oluwa Sofe";
    function writeName() {
      return fullName;
    }
    return writeName();
  }
  return sayName();
}

Комп’ютер викличе першу, другу чи третю змінну fullName?

Зверніть увагу: ви візьмете від цієї статті набагато більше, якщо випробуєте надані приклади на власній практиці.

Якщо у вас не буде виходити, не засмучуйтесь. Натомість, перегляньте урок та спробуйте ще раз.

Продовжуйте лише після того, як виклались на всі сто (якщо ви змухлюєте або проігноруєте цей крок, ви зробите гірше для себе!).

Впоралися?

Серед трьох визначень fullName, що представлені в коді вище, комп’ютер викличе та поверне ту змінну, яка визначена у функції sayName().

Змінна fullName всередині sayName() буде викликана, тому що sayName() є першою областю, в якій комп’ютер знайде визначення fullName.

Тому, якщо викликати profile(), поверненим значенням буде "Oluwa Sofe".

Спробуйте на StackBlitz

Запам’ятайте:

  • Уявімо, що комп’ютер не знайде визначення fullName у жодній області. В такому випадку комп’ютер поверне Uncaught ReferenceError: fullName is not defined.
  • Глобальна область завжди є останньою в ланцюгу областей. Іншими словами, глобальна область — це те місце, де закінчуються всі пошуки.
  • Внутрішня область (нащадок) має доступ до батьківської (зовнішньої) області, але зовнішня область не має доступу до області-нащадка. Наприклад, у фрагменті вище функція writeName() має доступ до коду всередині будь-якої батьківської області (sayName(), profile() або глобальної області). Однак sayName(), profile() та глобальна область не мають доступу до коду всередині writeName().

Швидкий огляд того, що ми вивчили

Область в JavaScript — це про простір.

Тому наступного разу, коли партнер запрошує вас у свою особисту область, пам’ятайте, що вони запрошують вас у свій особистий простір 😜!

Коли прибудете туди, не забудьте попросити їх продемонструвати лексичні вміння...

Ви питаєте, що означає «лексичний»? Давайте розберемося.

Що означає «лексичний»?

Лексичний це про визначення речей.

Все, що пов’язано зі створенням слів, виразів або змінних, є лексичним.

Наприклад, гра scrabble є лексичною активністю, оскільки вона пов’язана зі створенням слів.

Також особа, робота якої пов’язана з лінгвістикою (дослідженням мов), має лексичну кар’єру.

Зверніть увагу: другою назвою словника є лексикон. Іншими словами, лексикон — це словник, в якому перераховані та визначені слова.

Тепер, коли ми розуміємо значення слів «лексична» та«область», ми можемо говорити про лексичну область.

Що таке лексична область в JavaScript?

Лексична область — це область визначення виразу.

Іншими словами, лексична область предмету — це місце, в якому предмет було створено.

Зверніть увагу:

  • Інакшою назвою для лексичної області є статична область.
  • Місце, де предмет був викликаним, не завжди є лексичною областю предмета. Натомість область визначення предмету є його лексичною областю.

Приклад лексичної області:

Розглянемо такий код:

// Визначення змінної в глобальній області:
const myName = "Oluwatobi";

// Виклик змінної myName всередині функції:
function getName() {
  return myName;
}

Зверніть увагу, що у наведеному фрагменті ми визначили змінну myName в глобальній області та викликали її всередині функції getName().

Питання: який з двох просторів є лексичною областю змінної myName? Це глобальна область чи локальна область функції getName()?

Відповідь: не забувайте, що лексична область — це простір визначення, а не простір виклику. Тому лексичною областю змінної myName є глобальна область, оскільки ми визначили myName в глобальному середовищі.

Ще один приклад лексичної області

function getName() {
  const myName = "Oluwatobi";
  return myName;
}

Питання: де знаходиться лексична область myName?

Відповідь: зверніть увагу, що ми створили та викликали змінну myName всередині getName(). Тому лексичною областю myName є локальне серидовище getName(), оскільки getName() є простором визначення myName.

Як працює лексична область?

Область визначення виразу JavaScript визначає, який код має доступ до нього.

Іншими словами, лише код всередині лексичної області предмету має доступ до цього предмету.

Наприклад, розглянемо такий код:

// Визначення функції:
function showLastName() {
  const lastName = "Sofela";
  return lastName;
}

// Визначення іншої функції:
function displayFullName() {
  const fullName = "Oluwatobi " + lastName;
  return fullName;
}

// Виклик функції displayFullName():
console.log(displayFullName());

// Виклик функції вище поверне:
Uncaught ReferenceError: lastName is not defined

Зверніть увагу, що виклик функції displayFullName() у фрагменті вище повернув Uncaught ReferenceError. Помилка повертається, бо лише код всередині лексичної області предмету може мати доступ до предмету.

Як наслідок, ані функція displayFullName(), ані її внутрішній код не мають доступу до змінної lastName, оскільки lastName була визначена в іншій області.

Іншими словами, лексична область змінної lastName відрізняється від області змінної displayFullName().

Простір визначення lastName знаходиться у showLastName(), якщо лексична область displayFullName() — це глобальне серидовище.

Тепер розглянемо інший код:

// Визначення функції:
function showLastName() {
  const lastName = "Sofela";
  return lastName;
}

// Визначення іншої функції:
function displayFullName() {
  const fullName = "Oluwatobi " + showLastName();
  return fullName;
}

// Виклик функції displayFullName():
console.log(displayFullName());

// Виклик функції вище поверне:
"Oluwatobi Sofela"

У фрагменті вище displayFullName() успішно повернула "Oluwatobi Sofela", оскільки displayFullName() та showLastName() знаходяться в одній лексичній області.

Іншими словами, displayFullName() може викликати showLastName(), бо обидві функції визначено в глобальній області.

Зверніть увагу:

  • У другому прикладі displayFullName() не отримала доступу до змінної lastName всередині showLastName(). Натомість displayFullName() викликала showLastName(), яка потім повернула вміст змінної lastName.
  • Альтернативою лексичої області є динамічна область, але вона рідко використовується в програмуванні. Лише декілька мов, наприклад bash, використовують динамічну область.

Висновок

Кожен раз, коли ви чуєте «лексична», думайте про визначення.

Тобто лексична область машини, змінної, телефону, функції або купальника — це місце походження.

Огляд

У цій статті описано лексичну область в JavaScript. Також пояснено, чому це важливе поняття в програмуванні.

Дякую за увагу!