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

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

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

Начнем с областей видимости

Область видимости

Область видимости в JavaScript определяет, какие переменные доступны вам. Существуют два типа областей видимости: глобальная и локальная.

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

Если переменная объявлена вне всех функций или фигурных скобок ( <> ), то считается, что она определена в глобальной области видимости.

Примечание: это верно только для JavaScript в веб браузерах. В Node.js глобальные переменные объявляются иначе, но мы не будем касаться Node.js в этой статье.

const globalVariable = 'some value'; 

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

const hello = 'Hello CSS-Tricks Reader!'; function sayHello () < console.log(hello); >console.log(hello); // 'Hello CSS-Tricks Reader!' sayHello(); // 'Hello CSS-Tricks Reader!' 

Хотя можно объявлять переменные в глобальной области видимости, но не рекомендуется это делать. Всё из-за того, что существует вероятность пересечения имен, когда двум или более переменным присваивают одинаковое имя. Если переменные объявляются через const или let , то каждый раз, когда будет происходить пересечение имён, будет показываться сообщение об ошибке. Такое поведение нежелательно.

// Не делайте так! let thing = 'something'; let thing = 'something else'; // Ошибка, thing уже была объявлена 

Если объявлять переменные через var , то вторая переменная после объявления перепишет первую. Такое поведение тоже нежелательно, т.к. код усложняется в отладке.

// Не делайте так! var thing = 'something'; var thing = 'something else'; // возможно где-то в коде у переменной совершенно другое значение console.log(thing); // 'something else' 

Итак, следует всегда объявлять локальные переменные, а не глобальные.

Локальная область видимости

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

В JavaScript выделяют два типа локальных областей видимости:

Сначала рассмотрим область видимости функции

Область видимости функции

Переменная, объявленная внутри функции, доступна только внутри функции. Код снаружи функции не имеет к ней доступа.

Читайте также:  Запуск exe через python

В примере ниже, переменная hello находится внутри области видимости функции sayHello :

function sayHello () < const hello = 'Hello CSS-Tricks Reader!'; console.log(hello); >sayHello(); // 'Hello CSS-Tricks Reader!' console.log(hello); // Ошибка, hello не определена 

Область видимости блока

Переменная, объявленная внутри фигурных скобок <> через const или let , доступна только внутри фигурных скобок.

В примере ниже, можно увидеть, что переменная hello находится внутри области видимости фигурных скобок:

 < const hello = 'Hello CSS-Tricks Reader!'; console.log(hello); // 'Hello CSS-Tricks Reader!' >console.log(hello); // Ошибка, hello не определена 

Блочная область видимости является частным случаем области видимости функции, т.к. функции объявляются с фигурными скобками (кроме случаев использования стрелочных функций с неявным возвращением значения).

Подъем функции в области видимости

Функции, объявленные как «function declaration» (прим. перев.: функция вида function имя(параметры) ), всегда поднимаются наверх в текущей области видимости. Так, два примера ниже эквивалентны:

// Тоже самое, что пример ниже sayHello(); function sayHello () < console.log('Hello CSS-Tricks Reader!'); >// Тоже самое, что пример выше function sayHello () < console.log('Hello CSS-Tricks Reader!'); >sayHello(); 

Если же функция объявляется как «function expression» (функциональное выражение) (прим. перев.: функция вида var f = function (параметры) ), то такая функция не поднимается в текущей области видимости.

sayHello(); // Ошибка, sayHello не определена const sayHello = function ()

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

У функций нет доступа к областям видимости других функций

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

В примере ниже функция second не имеет доступа к переменной firstFunctionVariable .

function first () < const firstFunctionVariable = `I'm part of first`; >function second () < first(); console.log(firstFunctionVariable); // Ошибка, firstFunctionVariable не определена. >

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

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

В тоже время внешняя функция не имеет доступа к переменным внутренней функции.

function outerFunction () < const outer = `I'm the outer function!`; function innerFunction() < const inner = `I'm the inner function!`; console.log(outer); // I'm the outer function! >console.log(inner); // Ошибка, inner не определена > 

Для визуализации того, как работают области видимости, можно представить одностороннее зеркало. Вы можете видеть тех, кто находится с другой стороны, но те, кто стоят с обратной стороны (зеркальной стороны), не могут видеть вас.

Читайте также:  Python re findall list

Если одни области видимости вложены в другие, то это можно представить как множество стеклянных поверхностей с принципом действия, описанным выше.

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

Замыкания

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

function outerFunction () < const outer = `I see the outer variable!`; function innerFunction() < console.log(outer); >return innerFunction; > outerFunction()(); // I see the outer variable! 

Так как внутренняя функция является возвращаемым значением внешней функции, то можно немного сократить код, совместив возврат значения с объявлением функции.

function outerFunction () < const outer = `I see the outer variable!`; return function innerFunction() < console.log(outer); >> outerFunction()(); // I see the outer variable! 

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

Контроль побочных эффектов с помощью замыканий

Побочные эффекты появляются, когда производятся какие-то дополнительные действия помимо возврата значения после вызова функции. Множество вещей может быть побочным эффектом, например, Ajax-запрос, таймер или даже console.log:

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

Для пояснения рассмотрим пример

Допустим, требуется приготовить торт ко дню рождения вашего друга. Приготовление торта займет секунду, так как написанная функция выводит «торт испечён» через секунду.

Примечание: для краткости и простоты далее используются стрелочные функции из ES6.

function makeCake() < setTimeout(_ =>console.log(`Made a cake`), 1000); > 

Как можно заметить, такая функция имеет побочный эффект в виде таймера.

Далее допустим, вашему другу нужно выбрать вкус торта. Для этого нужно дописать «добавить вкус» к функции makeCake .

function makeCake(flavor) < setTimeout(_ =>console.log(`Made a $ cake!`), 1000); > 

После вызова функции торт будет испечён ровно через секунду.

makeCake('banana'); // Made a banana cake! 

Проблема в том, что, допустим, не нужно, чтобы торт был испечён сразу после уточнения вкуса, а необходимо, чтобы торт был испечён позже, когда это потребуется.

Читайте также:  Javascript object undefined value

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

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

function prepareCake (flavor) < return function () < setTimeout(_ =>console.log(`Made a $ cake!`), 1000); > > const makeCakeLater = prepareCake('banana'); // Позже в вашем коде. makeCakeLater(); // Made a banana cake! 

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

Приватные переменные с замыканиями

Как вы теперь знаете, переменные, созданные внутри функции, не могут быть доступны снаружи. Из-за того, что они не доступны, их также называют приватными переменными.

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

function secret (secretCode) < return < saySecretCode () < console.log(secretCode); >> > const theSecret = secret('CSS Tricks is amazing'); theSecret.saySecretCode(); // 'CSS Tricks is amazing' 

В примере выше saySecretCode — единственная функция (замыкание), которая выводит secretCode снаружи исходной функции secret. По этой причине такую функцию называют привилегированной.

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

Инструменты разработчика (DevTools) Chrome и Firefox упрощают отлаживание переменных в текущей области видимости. Существует два способа применения этого функционала.

Первый способ: добавлять ключевое слово debugger в код, чтобы останавливать выполнение JavaScript кода в браузерах с целью дальнейшей отладки.

Ниже пример с prepareCake :

function prepareCake (flavor) < // Добавляем debugger debugger return function () < setTimeout(_ =>console.log(`Made a $ cake!`), 1000); > > const makeCakeLater = prepareCake('banana'); 

Если открыть DevTools и перейти во вкладку Sources в Chrome (или вкладку Debugger в Firefox), то можно увидеть доступные переменные.

Можно также переместить debugger внутрь замыкания. Обратите внимание, как переменные области видимости изменяться в этот раз:

function prepareCake (flavor) < return function () < // Добавляем debugger debugger setTimeout(_ =>console.log(`Made a $ cake!`), 1000); > > const makeCakeLater = prepareCake('banana'); 

Второй способ: добавлять брейкпоинт напрямую в код во вкладке Sources (или Debugger) путем клика на номер строки.

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

Источник

Оцените статью