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


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


1691


источник


Ответы:


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

  1. Переменная с глобальным охватом

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

    // global scope
    var a = 1;
    
    function two(a) {
      // 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

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

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


2243



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

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

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


217



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

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


91



Старая школа 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известен только в первом цикле, но не раньше и не позже. Тем не менее, наша переменная iизвестно во всей функции.

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


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

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

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

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

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

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

    • Интернет-исследователь 10 и ниже (без поддержки)
    • Firefox 43 и ниже (без поддержки)
    • Safari 9 и ниже (без поддержки)
    • Opera Mini 8 и ниже (без поддержки)
    • Android-браузер 4 и ниже (без поддержки)
    • Опера 36 и ниже (частичная поддержка)
    • Chrome 51 и ниже (частичная поддержка)

enter image description here


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

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


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

(**) Заблокированные переменные диапазона не поднимаются


54



Вот пример:

<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>

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


33



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

Вот хорошая статья по этому вопросу.


26



In "Javascript 1.7" (Mozilla's extension to Javascript) one can also declare block-scope variables with let statement:

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

21



The idea of scoping in JavaScript when originally designed by Brendan Eich came from the HyperCard scripting language HyperTalk.

In this language, the displays were done similar to a stack of index cards. There was a master card referred to as the background. It was transparent and can be seen as the bottom card. Any content on this base card was shared with cards placed on top of it. Each card placed on top had its own content which took precedence over the previous card, but still had access to the prior cards if desired.

This is exactly how the JavaScript scoping system is designed. It just has different names. The cards in JavaScript are known as Execution ContextsECMA. Each one of these contexts contains three main parts. A variable environment, a lexical environment, and a this binding. Going back to the cards reference, the lexical environment contains all of the content from prior cards lower in the stack. The current context is at the top of the stack and any content declared there will be stored in the variable environment. The variable environment will take precedence in the case of naming collisions.

The this binding will point to the containing object. Sometimes scopes or execution contexts change without the containing object changing, such as in a declared function where the containing object may be window or a constructor function.

These execution contexts are created any time control is transferred. Control is transferred when code begins to execute, and this is primarily done from function execution.

So that is the technical explanation. In practice, it is important to remember that in JavaScript

  • Scopes are technically "Execution Contexts"
  • Contexts form a stack of environments where variables are stored
  • The top of the stack takes precedence (the bottom being the global context)
  • Each function creates an execution context (but not always a new this binding)

Applying this to one of the previous examples (5. "Closure") on this page, it is possible to follow the stack of execution contexts. In this example there are three contexts in the stack. They are defined by the outer context, the context in the immediately invoked function called by var six, and the context in the returned function inside of var six's immediately invoked function.

i) The outer context. It has a variable environment of a = 1
ii) The IIFE context, it has a lexical environment of a = 1, but a variable environment of a = 6 which takes precedence in the stack
iii) The returned function context, it has a lexical environment of a = 6 and that is the value referenced in the alert when called.

enter image description here


17



1) There is a global scope, a function scope, and the with and catch scopes. There is no 'block' level scope in general for variable's -- the with and the catch statements add names to their blocks.

2) Scopes are nested by functions all the way to the global scope.

3) Properties are resolved by going through the prototype chain. The with statement brings object property names into the lexical scope defined by the with block.

EDIT: ECMAAScript 6 (Harmony) is spec'ed to support let, and I know chrome allows a 'harmony' flag, so perhaps it does support it..

Let would be a support for block level scoping, but you have to use the keyword to make it happen.

EDIT: Based on Benjamin's pointing out of the with and catch statements in the comments, I've edited the post, and added more. Both the with and the catch statements introduce variables into their respective blocks, and that is a block scope. These variables are aliased to the properties of the objects passed into them.

 //chrome (v8)

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

EDIT: Clarifying example:

test1 is scoped to the with block, but is aliased to a.test1. 'Var test1' creates a new variable test1 in the upper lexical context (function, or global), unless it is a property of a -- which it is.

Yikes! Be careful using 'with' -- just like var is a noop if the variable is already defined in the function, it is also a noop with respect to names imported from the object! A little heads up on the name already being defined would make this much safer. I personally will never use with because of this.


15



I found that many people new to JavaScript have trouble understanding that inheritance is available by default in the language and that function scope is the only scope, so far. I provided an extension to a beautifier I wrote at the end of last year called JSPretty. The feature colors function scope in the code and always associates a color to all variables declared in that scope. Closure is visually demonstrated when a variable with a color from one scope is used in a different scope.

Try the feature at:

See a demo at:

View the code at:

Currently the feature offers support for a depth of 16 nested functions, but currently does not color global variables.


8



JavaScript have only two type of scope :

  1. Global Scope : Global is nothing but a window level scope.Here, variable present throughout the application.
  2. Functional Scope : Variable declared within a function with var keyword has functional scope.

Whenever a function is called, a variable scope object is created (and included in scope chain) which is followed by variables in JavaScript.

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

Scope chain -->

  1. Window level - a and outer function are at top level in scope chain.
  2. when outer function called a new variable scope object(and included in scope chain) added with variable b inside it.

Now when a variable a required it first searches for nearest variable scope and if variable is not there than it move's to next object of variable scope chain.which is in this case is window level.


7