Какова область видимости переменных в JavaScript?

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

29 ответов

Решение

Я думаю о том, что лучшее, что я могу сделать, это дать вам кучу примеров для изучения. Программисты Javascript практически ранжируются по тому, насколько хорошо они понимают сферу. Иногда это может быть довольно нелогичным.

  1. Глобальная переменная

    // global scope
    var a = 1;
    
    function one() {
      alert(a); // alerts '1'
    }
    
  2. Локальная сфера

    // global scope
    var a = 1;
    
    function two(a) { // passing (a) makes it local scope
      alert(a); // alerts the given argument, not the global value of '1'
    }
    
    // local scope again
    function three() {
      var a = 3;
      alert(a); // alerts '3'
    }
    
  3. Промежуточное звено: в JavaScript нет такой вещи, как область видимости блока (ES5; ES6 вводит let)

    а.

    var a = 1;
    
    function four() {
      if (true) {
        var a = 4;
      }
    
      alert(a); // alerts '4', not the global value of '1'
    }
    

    б.

    var a = 1;
    
    function one() {
      if (true) {
        let a = 4;
      }
    
      alert(a); // alerts '1' because the 'let' keyword uses block scoping
    }
    
  4. Средний: Свойства объекта

    var a = 1;
    
    function Five() {
      this.a = 5;
    }
    
    alert(new Five().a); // alerts '5'
    
  5. Дополнительно: Закрытие

    var a = 1;
    
    var six = (function() {
      var a = 6;
    
      return function() {
        // JavaScript "closure" means I have access to 'a' in here,
        // because it is defined in the function in which I was defined.
        alert(a); // alerts '6'
      };
    })();
    
  6. Дополнительно: разрешение области на основе прототипа

    var a = 1;
    
    function seven() {
      this.a = 7;
    }
    
    // [object].prototype.property loses to
    // [object].property in the lookup chain. For example...
    
    // Won't get reached, because 'a' is set in the constructor above.
    seven.prototype.a = -1;
    
    // Will get reached, even though 'b' is NOT set in the constructor.
    seven.prototype.b = 8;
    
    alert(new seven().a); // alerts '7'
    alert(new seven().b); // alerts '8'
    

  7. Global + Local: очень сложный случай

    var x = 5;
    
    (function () {
        console.log(x);
        var x = 10;
        console.log(x); 
    })();
    

    Это распечатает undefined а также 10 скорее, чем 5 а также 10 поскольку JavaScript всегда перемещает объявления переменных (не инициализации) в верхнюю часть области, делая код эквивалентным:

    var x = 5;
    
    (function () {
        var x;
        console.log(x);
        x = 10;
        console.log(x); 
    })();
    
  8. Переменная Catch-scoped

    var e = 5;
    console.log(e);
    try {
        throw 6;
    } catch (e) {
        console.log(e);
    }
    console.log(e);
    

    Это распечатает 5, 6, 5, Внутри оговорка поймать e тени глобальных и локальных переменных. Но эта специальная область предназначена только для перехваченной переменной. Если ты пишешь var f; внутри предложения catch это точно так же, как если бы вы определили его до или после блока try-catch.

Javascript использует цепочки областей видимости, чтобы установить область видимости для данной функции. Обычно существует одна глобальная область, и каждая определенная функция имеет свою собственную вложенную область. Любая функция, определенная в другой функции, имеет локальную область, которая связана с внешней функцией. Это всегда позиция в источнике, который определяет область действия.

Элемент в цепочке областей действия - это в основном Map с указателем на родительскую область видимости.

При разрешении переменной javascript начинается с самой внутренней области и выполняет поиск за ее пределами.

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

(Я уверен, что есть много тонкостей, на которые реальные программисты JavaScript смогут указать в других ответах. В частности, я наткнулся на эту страницу о том, что именно this значит в любое время. Надеюсь, что этой более вводной ссылки достаточно, чтобы начать работу.)

Старая школа JavaScript

Традиционно JavaScript имеет только два типа области видимости:

  1. Глобальная область: переменные известны во всем приложении с самого начала приложения (*)
  2. Функциональная область: переменные известны в функции, в которой они объявлены, с начала функции (*)

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


Современный JavaScript

Самые последние спецификации JavaScript теперь также допускают третью область:

  1. Область действия блока: переменные известны в блоке, в котором они объявлены, с момента их объявления (**)

Как создать переменные области видимости блока?

Традиционно вы создаете свои переменные следующим образом:

var myVariable = "Some text";

Переменные области видимости блока создаются следующим образом:

let myVariable = "Some text";

Так в чем же разница между функциональной областью и областью блока?

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

// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here

function loop(arr) {
    // i IS known here, but undefined
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( var i = 0; i < arr.length; i++ ) {
        // i IS known here, and has a value
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( let j = 0; j < arr.length; j++ ) {
        // i IS known here, and has a value
        // j IS known here, and has a value
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here
}

loop([1,2,3,4]);

for( var k = 0; k < arr.length; k++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS NOT known here
};

for( let l = 0; l < arr.length; l++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS known here, and has a value
};

loop([1,2,3,4]);

// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here

Здесь мы видим, что наша переменная j известен только в первом цикле for, но не до и после. Тем не менее, наша переменная i известен во всей функции.

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


Безопасно ли сегодня использовать блочные переменные области видимости?

Насколько безопасно использовать сегодня, зависит от вашей среды:

  • Если вы пишете код JavaScript на стороне сервера ( Node.js), вы можете безопасно использовать let заявление.

  • Если вы пишете код JavaScript на стороне клиента и используете транспортер (например, Traceur), вы можете смело использовать let заявление, однако ваш код, вероятно, будет не оптимальным с точки зрения производительности.

  • Если вы пишете код JavaScript на стороне клиента и не используете транспортер, вам следует подумать о поддержке браузера.

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

    • Internet Explorer 10 и ниже (без поддержки)
    • Firefox 43 и ниже (без поддержки)
    • Safari 9 и ниже (без поддержки)
    • Opera Mini 8 и ниже (без поддержки)
    • Android-браузер 4 и ниже (без поддержки)
    • Opera 36 и ниже (частичная поддержка)
    • Chome 51 и ниже (частичная поддержка)


Как отслеживать поддержку браузера

Для актуального обзора, какие браузеры поддерживают let заявление во время прочтения этого ответа, см. это Can I Use страница


(*) Глобальные и функциональные переменные могут быть инициализированы и использованы до того, как объявлены, потому что переменные JavaScript подняты. Это означает, что объявления всегда находятся на вершине области видимости.

(**) Переменные области видимости не подняты

Вот пример:

<script>

var globalVariable = 7; //==window.globalVariable

function aGlobal( param ) { //==window.aGlobal(); 
                            //param is only accessible in this function
  var scopedToFunction = {
    //can't be accessed outside of this function

    nested : 3 //accessible by: scopedToFunction.nested
  };

  anotherGlobal = {
    //global because there's no `var`
  }; 

}

</script>

Вы захотите исследовать замыкания и узнать, как их использовать для создания частных пользователей.

Ключ, насколько я понимаю, состоит в том, что Javascript имеет определение уровня функции по сравнению с более распространенной областью видимости блока C.

Вот хорошая статья на эту тему.

В "Javascript 1.7" (расширение Mozilla до Javascript) можно также объявить переменные области блока с помощью let утверждение:

 var a = 4;
 let (a = 3) {
   alert(a); // 3
 }
 alert(a);   // 4

Идея области видимости в JavaScript, изначально разработанная Бренданом Айхом, возникла из языка сценариев HyperCard HyperTalk.

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

Именно так и разработана система определения объема JavaScript. У него просто разные имена. Карты в JavaScript известны как контексты исполненияECMA. Каждый из этих контекстов состоит из трех основных частей. Переменная среда, лексическая среда и привязка this. Возвращаясь к справочнику по картам, лексическая среда содержит весь контент из предыдущих карт ниже в стопке. Текущий контекст находится на вершине стека, и любой объявленный там контент будет храниться в переменной среде. Переменная среда будет иметь приоритет в случае именования коллизий.

Привязка this будет указывать на содержащий объект. Иногда контексты или контексты выполнения изменяются без изменения содержащего объекта, например, в объявленной функции, где содержащийся объект может быть window или функция конструктора.

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

Так что это техническое объяснение. На практике важно помнить, что в JavaScript

  • Области технически "контексты исполнения"
  • Контексты образуют стек сред, в которых хранятся переменные
  • Вершина стека имеет приоритет (нижняя часть является глобальным контекстом)
  • Каждая функция создает контекст выполнения (но не всегда новый, это связывание)

Применяя это к одному из предыдущих примеров (5. "Закрытие") на этой странице, можно следовать стекам контекстов выполнения. В этом примере в стеке три контекста. Они определяются внешним контекстом, контекстом в немедленно вызываемой функции, вызываемой var шестым, и контекстом в возвращаемой функции внутри немедленно вызываемой функции var шестого.

я) Внешний контекст. Имеет переменную среду с = 1
ii) контекст IIFE, он имеет лексическую среду a = 1, но переменную среду a = 6, которая имеет приоритет в стеке
iii) Возвращенный контекст функции, он имеет лексическую среду a = 6, и это значение, на которое ссылается предупреждение при вызове.

1) Существует глобальная область действия, область действия функции, а также области действия with и catch. В общем случае для переменных нет области уровня "блок" - операторы with и catch добавляют имена в свои блоки.

2) Области применения вложены функциями вплоть до глобальной области видимости.

3) Свойства определяются путем прохождения цепочки прототипов. Оператор with переводит имена свойств объекта в лексическую область, определенную блоком with.

РЕДАКТИРОВАТЬ: ECMAAScript 6 (Harmony) предназначен для поддержки let, и я знаю, что Chrome позволяет флаг 'Harmony', так что, возможно, он поддерживает его..

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

РЕДАКТИРОВАТЬ: Исходя из того, что Бенджамин указал на высказывания with и catch в комментариях, я отредактировал пост и добавил больше. Оба оператора with и catch вводят переменные в соответствующие блоки, и это является областью видимости блоков. Эти переменные связываются со свойствами переданных в них объектов.

 //chrome (v8)

 var a = { 'test1':'test1val' }
 test1   // error not defined
 with (a) { var test1 = 'replaced' }
 test1   // undefined
 a       // a.test1 = 'replaced'

РЕДАКТИРОВАТЬ: Уточняющий пример:

test1 ограничен блоком with, но имеет псевдоним a.test1. "Var test1" создает новую переменную test1 в верхнем лексическом контексте (функция или глобальный), если только она не является свойством a, которым она является.

Хлоп! Будьте осторожны, используя 'with' - точно так же, как var - noop, если переменная уже определена в функции, это также noop относительно имен, импортируемых из объекта! Небольшое упоминание имени, которое уже определено, сделает это намного безопаснее. Я лично никогда не буду использовать с из-за этого.

Очень распространенная еще не описанная проблема, с которой часто сталкиваются интерфейсные кодеры, - это область, видимая встроенному обработчику событий в HTML, например, с

<button onclick="foo()"></button>

Объем переменных, которые on*Атрибут может ссылаться, должен быть либо:

  • global (рабочие встроенные обработчики почти всегда ссылаются на глобальные переменные)
  • свойство документа (например, querySelector как отдельная переменная будет указывать на document.querySelector; редко)
  • свойство элемента, к которому прикреплен обработчик (как и выше; редко)

В противном случае вы получите ошибку ReferenceError при вызове обработчика. Так, например, если встроенный обработчик ссылается на функцию, которая определена внутри window.onload или $(function() {, ссылка не удастся, потому что встроенный обработчик может ссылаться только на переменные в глобальной области, а функция не является глобальной:

window.addEventListener('DOMContentLoaded', () => {
  function foo() {
    console.log('foo running');
  }
});
<button onclick="foo()">click</button>

Свойства documentи свойства элемента, к которому прикреплен обработчик, также могут упоминаться как автономные переменные внутри встроенных обработчиков, поскольку встроенные обработчики вызываются внутри двухwithблоки, один дляdocument, один для элемента. Цепочка областей видимости переменных внутри этих обработчиков крайне неинтуитивна, и для рабочего обработчика событий, вероятно, потребуется, чтобы функция была глобальной (и, вероятно, следует избегать ненужного глобального загрязнения).

Поскольку цепочка областей видимости внутри встроенных обработчиков настолько странная, и поскольку встроенные обработчики требуют для работы глобального загрязнения, а также поскольку встроенные обработчики иногда требуют уродливого экранирования строки при передаче аргументов, вероятно, их легче избежать. Вместо этого прикрепите обработчики событий с помощью Javascript (например, сaddEventListener), а не с разметкой HTML.

function foo() {
  console.log('foo running');
}
document.querySelector('.my-button').addEventListener('click', foo);
<button class="my-button">click</button>


На другой ноте, в отличие от обычного <script>Теги, которые выполняются на верхнем уровне, код внутри модулей ES6 выполняется в своей собственной частной области. Переменная, определенная в верхней части обычного<script> тег является глобальным, поэтому вы можете ссылаться на него в других <script> теги, например:

<script>
const foo = 'foo';
</script>
<script>
console.log(foo);
</script>

Но верхний уровень модуля ES6 не глобален. Переменная, объявленная в верхней части модуля ES6, будет видна только внутри этого модуля, если переменная не указана явно.exported, или если он не назначен свойству глобального объекта.

<script type="module">
const foo = 'foo';
</script>
<script>
// Can't access foo here, because the other script is a module
console.log(typeof foo);
</script>

Верхний уровень модуля ES6 аналогичен внутреннему уровню IIFE на верхнем уровне в обычном <script>. Модуль может ссылаться на любые глобальные переменные, и ничто не может ссылаться ни на что внутри модуля, если модуль явно не предназначен для этого.

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

Попробуйте эту функцию на:

Посмотреть демо на:

Посмотреть код на:

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

JavaScript имеет только два типа области видимости:

  1. Global Scope: Global - это не что иное, как область действия на уровне окна. Здесь переменная присутствует во всем приложении.
  2. Функциональная область: переменная, объявленная в функции с var Ключевое слово имеет функциональную область.

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

        a = "global";
         function outer(){ 
              b = "local";
              console.log(a+b); //"globallocal"
         }
outer();

Цепочка прицела ->

  1. Уровень окна - a а также outer Функция находится на верхнем уровне в цепочке области видимости.
  2. когда внешняя функция вызвала новую variable scope object(и включен в цепочку областей действия), добавлен с переменной b внутри него.

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

Глобальная сфера:

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

//global variable
var a = 2;

//global function
function b(){
   console.log(a);  //access global variable
}

Местный охват:

Если вы находитесь в США, вы можете знать Ким Кардашьян, печально известную знаменитость (ей каким-то образом удается создавать таблоиды). Но люди за пределами США не узнают ее. Она местная звезда, привязанная к своей территории.

Локальные переменные похожи на локальные звезды. Вы можете получить к ним доступ только (получить или установить значение) внутри области. Локальная функция похожа на локальные события - вы можете выполнять только (праздновать) внутри этой области. Если вы хотите получить к ним доступ за пределами области действия, вы получите ошибку ссылки

function b(){
   var d = 21; //local variable
   console.log(d);

   function dog(){  console.log(a); }
     dog(); //execute local function
}

 console.log(d); //ReferenceError: dddddd is not defined    

Проверьте эту статью для глубокого понимания области

Запустить код. надеюсь, что это даст представление о области видимости

Name = 'global data';
document.Name = 'current document data';
(function(window,document){
var Name = 'local data';
var myObj = {
    Name: 'object data',
    f: function(){
        alert(this.Name);
    }
};

myObj.newFun = function(){
    alert(this.Name);
}

function testFun(){
    alert("Window Scope : " + window.Name + 
          "\nLocal Scope : " + Name + 
          "\nObject Scope : " + this.Name + 
          "\nCurrent document Scope : " + document.Name
         );
}


testFun.call(myObj);
})(window,document);

Просто чтобы добавить к другим ответам, область действия - это список поиска всех объявленных идентификаторов (переменных), и обеспечивает строгий набор правил относительно того, как они доступны для выполняемого в настоящее время кода. Этот поиск может быть использован для назначения переменной, которая является ссылкой LHS (слева), или для получения ее значения, которая является ссылкой RHS (правой стороны). Эти поиски - то, что движок JavaScript делает внутренне, когда он компилирует и выполняет код.

Таким образом, с этой точки зрения, я думаю, что поможет картина, которую я нашел в электронной книге Scopes and Closures Кайла Симпсона:

образ

Цитирую из его книги:

Здание представляет собой набор правил для вложенной области действия нашей программы. Первый этаж здания представляет вашу текущую область действия, где бы вы ни находились. Верхний уровень здания - глобальная сфера. Вы решаете рекомендации LHS и RHS, просматривая свой текущий этаж, а если вы его не находите, поднимаетесь на лифте на следующий этаж, смотрите там, затем на следующий и так далее. Как только вы попадаете на верхний этаж (глобальный охват), вы либо находите то, что ищете, либо нет. Но ты должен остановиться независимо.

Стоит упомянуть одну вещь: "Поиск в области видимости прекращается, когда он находит первое совпадение".

Эта идея "уровней области действия" объясняет, почему "это" можно изменить с помощью вновь созданной области действия, если она ищется во вложенной функции. Вот ссылка, которая входит во все эти детали, все, что вы хотели знать о сфере действия JavaScript

ПОЧТИ только два типа областей JavaScript:

  • область действия каждого объявления var связана с наиболее непосредственно включающей функцией
  • если для объявления var нет включающей функции, это глобальная область

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

var i = 10, v = 10;
for (var i = 0; i < 5; i++) { var v = 5; }
console.log(i, v);
// output 5 5

Вместо этого используйте функции:

var i = 10, v = 10;
$.each([0, 1, 2, 3, 4], function(i) { var v = 5; });
console.log(i,v);
// output 10 10

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

Это почти все, что вам нужно знать с точки зрения JavaScript, за исключением:

  • try / catch вводит новую область видимости ТОЛЬКО для самой переменной исключения, другие переменные не имеют новой области видимости
  • with-clause, очевидно, является еще одним исключением, но использование with-clause крайне нежелательно ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with)

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

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

Итак, этот код:

var i = 1;
function abc() {
  i = 2;
  var i = 3;
}
console.log(i);     // outputs 1

эквивалентно:

var i = 1;
function abc() {
  var i;     // var declaration moved to the top of the scope
  i = 2;
  i = 3;     // the assignment stays where it is
}
console.log(i);

Это может показаться нелогичным, но это имеет смысл с точки зрения императора языкового конструктора.

Modern Js, ES6 +, 'const' а также 'let'

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

const следует использовать в 95% случаев. Это делает так, чтобы ссылка на переменную не могла измениться. Свойства массива, объекта и узла DOM могут изменяться и, скорее всего, const,

let следует использовать для любой переменной, ожидающей переназначения. Это включает в себя цикл for. Если вы когда-либо измените значение после инициализации, используйте let,

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

Попробуйте этот любопытный пример. В приведенном ниже примере, если бы a было числовым значением, инициализированным в 0, вы бы увидели 0, а затем 1. За исключением того, что a является объектом, и javascript передаст f1 указатель, а не его копию. В результате вы получаете одно и то же предупреждение оба раза.

var a = new Date();
function f1(b)
{
    b.setDate(b.getDate()+1);
    alert(b.getDate());
}
f1(a);
alert(a.getDate());

С каждым фрагментом кода JavaScript (глобальным кодом или функциями) связана цепочка областей действия. Эта цепочка областей видимости представляет собой список или цепочку объектов, которые определяют переменные, которые находятся "в области видимости" для этого кода. Когда JavaScript должен искать значение переменной x (процесс называется переменным разрешением), он начинается с просмотра первого объекта в цепочке. Если этот объект имеет свойство с именем x, значение этого свойства используется. Если первый объект не имеет свойства с именем x JavaScript продолжает поиск со следующего объекта в цепочке. Если второй объект не имеет свойства с именем x поиск переходит к следующему объекту и так далее. Если x не является свойством какого-либо из объектов в цепочке областей действия, то x находится вне области действия этого кода, и возникает ошибка ReferenceError. В коде JavaScript верхнего уровня (т. Е. В коде, который не содержится ни в одном из определений функций) цепочка областей действия состоит из одного объекта - глобального объекта. В не вложенной функции цепочка областей действия состоит из двух объектов. Первый - это объект, который определяет параметры функции и локальные переменные, а второй - глобальный объект. Во вложенной функции цепочка областей действия имеет три или более объектов. Важно понимать, как создается эта цепочка объектов. Когда функция определена, она сохраняет действующую цепочку областей действия. Когда эта функция вызывается, она создает новый объект для хранения своих локальных переменных и добавляет этот новый объект в цепочку хранимой области, чтобы создать новую, более длинную цепочку, представляющую область для вызова этой функции. Это становится более интересным для вложенных функций, потому что каждый раз, когда вызывается внешняя функция, внутренняя функция определяется снова. Поскольку цепочка областей видимости различается при каждом вызове внешней функции, внутренняя функция будет слегка отличаться при каждом ее определении - код внутренней функции будет одинаковым при каждом вызове внешней функции, но с ней связана цепочка областей действия. код будет другим. Это понятие цепочки областей действия имеет решающее значение для понимания замыканий.

В JS есть только функциональные области. Не блокируйте прицелы! Вы можете увидеть, что поднимает тоже.

var global_variable = "global_variable";
var hoisting_variable = "global_hoist";

// Global variables printed
console.log("global_scope: - global_variable: " + global_variable);
console.log("global_scope: - hoisting_variable: " + hoisting_variable);

if (true) {
    // The variable block will be global, on true condition.
    var block = "block";
}
console.log("global_scope: - block: " + block);

function local_function() {
    var local_variable = "local_variable";
    console.log("local_scope: - local_variable: " + local_variable);
    console.log("local_scope: - global_variable: " + global_variable);
    console.log("local_scope: - block: " + block);
    // The hoisting_variable is undefined at the moment.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);

    var hoisting_variable = "local_hoist";
    // The hoisting_variable is now set as a local one.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);
}

local_function();

// No variable in a separate function is visible into the global scope.
console.log("global_scope: - local_variable: " + local_variable);

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

Для тех, кто считает, что существует только глобальная и локальная область видимости, пожалуйста, объясните, почему у Mozilla будет целая страница, описывающая нюансы области видимости блока в JS.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

ES5 и раньше:

Переменные в Javascript были изначально (предварительно ES6) лексическая функция ограничена. Термин "лексически ограниченный" означает, что вы можете увидеть область действия переменных, "посмотрев" на код.

Каждая переменная, объявленная с var Ключевое слово распространяется на функцию. Однако, если в этой функции объявлена ​​другая функция, эти функции будут иметь доступ к переменным внешних функций. Это называется цепочкой контекста. Это работает следующим образом:

  1. Когда функция ищет разрешение переменной, она сначала смотрит на свою область видимости. Это тело функции, то есть все, что находится внутри фигурных скобок {} (за исключением переменных внутри других функций, которые находятся в этой области).
  2. Если он не может найти переменную внутри тела функции, он поднимется до цепочки и рассмотрит область действия переменной в функции, в которой была определена функция. Это то, что подразумевается под лексической областью действия, мы можем видеть в коде, где была определена эта функция, и, таким образом, можем определять цепочку области действия, просто просматривая код.

Пример:

// global scope
var foo = 'global';
var bar = 'global';
var foobar = 'global';

function outerFunc () {
 // outerFunc scope
 var foo = 'outerFunc';
 var foobar = 'outerFunc';
 innerFunc();
 
 function innerFunc(){
 // innerFunc scope
  var foo = 'innerFunc';
  console.log(foo);
  console.log(bar);
  console.log(foobar);
  }
}

outerFunc();

Что происходит, когда мы пытаемся регистрировать переменные foo, bar, а также foobar на консоль идет следующее:

  1. Мы пытаемся войти в консоль foo, foo можно найти внутри функции innerFunc сам. Следовательно, значение foo разрешается в строку innerFunc,
  2. Мы пытаемся войти бар в консоль, бар не может быть найден внутри функции innerFunc сам. Поэтому нам нужно подняться по цепочке прицелов. Сначала мы посмотрим на внешнюю функцию, в которой функция innerFunc был определен. Это функция outerFunc, В объеме outerFunc мы можем найти переменную bar, которая содержит строку "externalFunc".
  3. foobar не может быть найден во innerFunc., Поэтому нам нужно подняться по цепочке областей действия к области видимости innerFunc. Это также не может быть найдено здесь, мы поднимаемся на другой уровень к глобальной области видимости (то есть к самой внешней области видимости). Здесь мы находим переменную foobar, которая содержит строку "global". Если он не найдет переменную после перехода по цепочке областей действия, механизм JS выдаст referenceError.

ES6 (ES 2015) и старше:

Те же самые понятия лексического охвата и области охвата все еще применяются в ES6, Однако были введены новые способы объявления переменных. Есть следующее:

  • let: создает переменную области блока
  • const: создает переменную области блока, которая должна быть инициализирована и не может быть переназначена

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

let letVar = 'global';
var varVar = 'global';

function foo () {
  
  if (true) {
    // this variable declared with let is scoped to the if block, block scoped
    let letVar = 5;
    // this variable declared with let is scoped to the function block, function scoped
    var varVar = 10;
  }
  
  console.log(letVar);
  console.log(varVar);
}


foo();

В приведенном выше примере letVar регистрирует глобальное значение, потому что переменные объявлены с let имеют ограниченную область видимости. Они перестают существовать вне соответствующего блока, поэтому к переменной нельзя получить доступ вне блока if.

В JavaScript есть два типа области видимости:

  • Локальная сфера
  • Глобальная сфера

Функция Below имеет локальную переменную области видимости carName, И эта переменная не доступна извне функции.

function myFunction() {
    var carName = "Volvo";
    alert(carName);
    // code here can use carName
}

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

class {

    var carName = " Volvo";

    // code here can use carName

    function myFunction() {
        alert(carName);
        // code here can use carName 
    }
}

Мне очень нравится принятый ответ, но я хочу добавить следующее:

Scope собирает и поддерживает список поиска всех объявленных идентификаторов (переменных) и обеспечивает соблюдение строгого набора правил в отношении того, как они доступны для текущего выполняющегося кода.

Область видимости - это набор правил для поиска переменных по их имени идентификатора.

  • Если переменная не может быть найдена в непосредственной области видимости, Engine обращается к следующей внешней содержащей область видимости, продолжая до тех пор, пока не будет найдена или пока не будет достигнута самая внешняя (также известная как глобальная) область.
  • Набор правил, определяющих, где и как можно искать переменную (идентификатор). Этот поиск может быть для целей присвоения переменной, которая является ссылкой LHS (левая сторона), или может быть для целей получения ее значения, которое является ссылкой RHS (правая сторона)..
  • Ссылки LHS являются результатом операций присваивания. Присваивания, связанные с областью действия, могут происходить либо с помощью оператора =, либо путем передачи аргументов (присваивания) параметрам функции.
  • Механизм JavaScript сначала компилирует код перед его выполнением и при этом разбивает такие операторы, как var a = 2; на два отдельных шага: 1-й. Во-первых, var a, чтобы объявить его в этой области. Это выполняется в начале, до выполнения кода. 2-й. Позже a = 2 для поиска переменной (ссылка LHS) и присвоения ей, если она найдена.
  • Поиск ссылок как LHS, так и RHS начинается с текущей выполняемой области, и при необходимости (т. Е. Они не находят там то, что ищут), они продвигаются вверх по вложенной области, одна область (этаж) за один раз, ища идентификатор, пока они не дойдут до глобального (верхнего этажа) и не остановятся, и либо найдут его, либо нет. Невыполненные ссылки RHS приводят к возникновению ошибки ReferenceError. Невыполненные ссылки LHS приводят к автоматическому, неявно созданному глобалу с таким именем (если не в строгом режиме) или ReferenceError (если в строгом режиме).
  • Область видимости состоит из серии "пузырей", каждый из которых действует как контейнер или ведро, в которых объявляются идентификаторы (переменные, функции). Эти пузыри аккуратно вкладываются друг в друга, и это расположение определяется во время автора.

В EcmaScript5 в основном есть две области: локальная область и глобальная область, но в EcmaScript6 у нас есть в основном три области: локальная область, глобальная область и новая область, называемая областью блока.

Пример объема блока:

for ( let i = 0; i < 10; i++)
{
 statement1...
statement2...// inside this scope we can access the value of i, if we want to access the value of i outside for loop it will give undefined.
}

В ECMAScript 6 введены ключевые слова let и const. Эти ключевые слова могут использоваться вместо ключевого слова var. В отличие от ключевого слова var ключевые слова let и const поддерживают объявление локальной области видимости внутри операторов блока.

var x = 10
let y = 10
const z = 10
{
  x = 20
  let y = 20
  const z = 20
  {
    x = 30
    // x is in the global scope because of the 'var' keyword
    let y = 30
    // y is in the local scope because of the 'let' keyword
    const z = 30
    // z is in the local scope because of the 'const' keyword
    console.log(x) // 30
    console.log(y) // 30
    console.log(z) // 30
  }
  console.log(x) // 30
  console.log(y) // 20
  console.log(z) // 20
}

console.log(x) // 30
console.log(y) // 10
console.log(z) // 10

В JavaScript есть два типа областей действия.

  1. Глобальная область: переменная, объявленная в глобальной области, может использоваться в любом месте программы очень плавно. Например:

    var carName = " BMW";
    
    // code here can use carName
    
    function myFunction() {
         // code here can use carName 
    }
    
  2. Функциональная область или локальная область: переменная, объявленная в этой области, может использоваться только в своей собственной функции. Например:

    // code here can not use carName
    function myFunction() {
       var carName = "BMW";
       // code here can use carName
    }
    

Global: переменная, объявленная вне функции

Local: переменная объявлена ​​внутри функции и может быть вызвана только в этой области

Другие вопросы по тегам