Javascript функция определения объема и подъема - PullRequest
82 голосов
/ 22 сентября 2011

Я только что прочитал замечательную статью о JavaScript Scoping and Hoisting от Бен Черри , в которой он приводит следующий пример:

var a = 1;

function b() {
    a = 10;
    return;

    function a() {}
}
b();
alert(a);

Используя приведенный выше код, браузер выдаст предупреждение «1».

Я до сих пор не уверен, почему он возвращает «1». Некоторые вещи, которые он говорит, приходят на ум, как: Все объявления функций поднимаются наверх. Вы можете охватить переменную с помощью функции. Все еще не щелкает для меня.

Ответы [ 15 ]

111 голосов
/ 22 сентября 2011

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

function b() {  
   a = 10;  
   return;  
   function a() {} 
} 

будет переписан интерпретатором в это

function b() {
  function a() {}
  a = 10;
  return;
}

Странно, а?

Также, в этом случае,

function a() {}

вел себя так же, как

var a = function () {};

Итак, по сути, это то, что делает код:

var a = 1;                 //defines "a" in global scope
function b() {  
   var a = function () {}; //defines "a" in local scope 
   a = 10;                 //overwrites local variable "a"
   return;      
}       
b();       
alert(a);                 //alerts global variable "a"
6 голосов
/ 22 сентября 2011

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

function a() {} 

действительно становится

var a = function () {}

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

5 голосов
/ 22 сентября 2011

Функция a поднята внутри функции b:

var a = 1; 
function b() { 
   function a() {} 
   a = 10; 
   return;
} 
b(); 
alert(a);

, что почти как использование var:

var a = 1; 
function b() { 
   var a = function () {};
   a = 10; 
   return;
} 
b(); 
alert(a);

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

3 голосов
/ 17 мая 2014
  1. объявление функции function a(){} поднимается первым и ведет себя как var a = function () {};, следовательно, в локальной области видимости a создается.
  2. Если у вас есть две переменные с одинаковым именем (одна в глобальной, другая в локальной), локальная переменная всегда имеет приоритет над глобальной переменной.
  3. Когда вы устанавливаете a=10, вы устанавливаете локальную переменную a, а не глобальную.

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

1 голос
/ 20 апреля 2017

Что является яблоком раздора в этом небольшом фрагменте кода?

Случай 1:

Включить определение function a(){} в теле function b какследующим образом.logs value of a = 1

var a = 1;
function b() {
  a = 10;
  return;

  function a() {}
}
b();
console.log(a); // logs a = 1

Случай 2

Исключить определение function a(){} внутри тела function b следующим образом.logs value of a = 10

var a = 1;
function b() {
  a = 10;  // overwrites the value of global 'var a'
  return;
}
b();
console.log(a); // logs a = 10

Наблюдение поможет вам понять, что утверждение console.log(a) регистрирует следующие значения.

Дело 1: a = 1

Дело 2: a = 10

Позиции

  1. var a было определено и объявлено лексически в глобальной области видимости.
  2. a=10 Этот оператор переназначает значение 10, он лексически находится внутри функции b.

Объяснение обоих случаев

Поскольку function definition with name property a совпадает с variable a.variable a внутри function body b становится локальной переменной.Предыдущая строка подразумевает, что глобальное значение a остается неизменным, а локальное значение a обновляется до 10.

Итак, мы собираемся сказать, что код ниже

var a = 1;
function b() {
  a = 10;
  return;

  function a() {}
}
b();
console.log(a); // logs a = 1

Интерпретатор JS интерпретирует его следующим образом.

var a = 1;
function b() {
  function a() {}
  a = 10;
  return;


}
b();
console.log(a); // logs a = 1

Однако, когда мы удаляем function a(){} definition, value of 'a', объявленные и определенные вне функции b, это значение перезаписывается и изменяется на 10в случае 2. Значение перезаписывается, потому что a=10 относится к глобальному объявлению, и если оно должно быть объявлено локально, мы должны написать var a = 10;.

var a = 1;
function b() {
  var a = 10; // here var a is declared and defined locally because it uses a var keyword. 
  return;
}
b();
console.log(a); // logs a = 1

Мы можем прояснить наши сомнения, изменив name property in function a(){} definition на другое имя, отличное от 'a'

var a = 1;
function b() {
  a = 10; // here var a is declared and defined locally because it uses a var keyword. 
  return;

  function foo() {}
}
b();
console.log(a); // logs a = 1
1 голос
/ 17 декабря 2016

scpope, закрытие и подъем (вар / функция)

  1. scpope: глобальная переменная может быть доступна в любом месте (весь файл область видимости), локальная переменная может быть доступна только локальным область (функция / область блока)!
    Примечание: если локальная переменная не используется ключевые слова var в функции, она станет глобальной переменной!
  2. закрытие: функция внутри другой функции, которая может получить доступ локальная область видимости (родительская функция) и глобальная область видимости не могут быть доступны другим! если только вы не вернете его как возвращаемое значение!
  3. подъем: переместить все объявляемые / отменяемые переменные / функции в область видимости top, чем присвоить значение или ноль!
    Примечание: он просто перемещает объявление, а не перемещает значение!

var a = 1;                
//"a" is global scope
function b() {  
   var a = function () {}; 
   //"a" is local scope 
   var x = 12; 
   //"x" is local scope 
   a = 10;
   //global variable "a" was overwrited by the local variable "a"  
   console.log("local a =" + a);
   return console.log("local x = " + x);
}       
b();
// local a =10
// local x = 12
console.log("global a = " + a);
// global a = 1
console.log("can't access local x = \n");
// can't access local x = 
console.log(x);
// ReferenceError: x is not defined
1 голос
/ 14 апреля 2015

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

Когда объявления происходят, var a, затем function b и внутри этой области b объявляется function a.

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

После того, как объявления сделаны, начнется присвоение значений, глобальное a получит значение 1, а внутреннее function b получит 10. когда вы делаете alert(a), он вызывает фактическую глобальную переменную области видимости. Это небольшое изменение в коде сделает его более понятным

        var a = 1;

    function b() {
        a = 10;
        return a;

        function a() { }
    }

    alert(b());
    alert(a);
1 голос
/ 22 сентября 2011

function a() { } - это оператор функции, который создает переменную a, локальную для функции b.
Переменные создаются при анализе функции независимо от того, выполняется оператор var или функция..

a = 10 устанавливает эту локальную переменную.

0 голосов
/ 13 февраля 2019

Удивительно, но ни один из ответов здесь не упоминает актуальность контекста исполнения в цепочке областей действия.

Механизм JavaScript упаковывает исполняемый в данный момент код в контекст выполнения. Базовый контекст выполнения - это глобальный контекст выполнения. Каждый раз, когда вызывается новая функция, создается новый контекст выполнения и помещается в стек выполнения. Подумайте о стеке стека, расположенном в стеке вызовов на других языках программирования. Последний в первом вышел. Теперь каждый контекст выполнения имеет свою собственную переменную среду и внешнюю среду в JavaScript.

Я буду использовать приведенный ниже пример в качестве демонстрации.

1) Сначала мы входим в фазу создания глобального контекста выполнения. И внешняя среда, и переменная среда лексической среды созданы. Глобальный объект настраивается и помещается в память со специальной переменной «this», указывающей на него. Функция a и ее код, а также переменная myVar с неопределенным значением помещаются в память в глобальной переменной среде. важно отметить, что код функции а не выполняется. Он просто помещается в память с функцией a.

2) Во-вторых, это фаза выполнения контекста выполнения. myVar больше не является неопределенным значением. Он инициализируется значением 1, которое хранится в глобальной переменной среде. Вызывается функция a и создается новый контекст выполнения.

3) В контексте выполнения функции а она проходит этап создания и выполнения своего собственного контекста выполнения. Он имеет свою собственную внешнюю среду и переменную среду, то есть свою собственную лексическую среду. Функция b и переменная myVar хранятся в своей переменной среды. Эта переменная среда отличается от глобальной переменной среды. Поскольку функция a лексически (физически в коде) находится на том же уровне, что и глобальный контекст выполнения, ее внешняя среда является глобальным контекстом выполнения. Таким образом, если функция a должна была ссылаться на переменную, отсутствующую в ее среде переменных, она будет искать цепочку областей действия и пытаться найти переменную в среде переменных глобального контекста выполнения.

4) Функция b вызывается в функции a. Новый контекст выполнения создан. Поскольку он находится в функции a лексически, его Внешняя среда - это. Поэтому, когда он ссылается на myVar, так как myVar не находится в Переменной среде функции b, он будет выглядеть в Переменной среде функции a. Он находит его там и console.log печатает 2. Но если переменная не была в Переменной среде функции a, то, поскольку Внешняя среда функции a является глобальным Контекстом выполнения, тогда Цепочка Области продолжит поиск там.

5) После завершения выполнения функций b и a они извлекаются из стека выполнения. Однопоточный движок JavaScript продолжает выполнение в глобальном контексте выполнения. Он вызывает функцию b. Но в глобальной среде переменных нет функции b, и нет другой внешней среды для поиска в глобальном контексте выполнения. Таким образом, механизм JavaScript создает исключение.

function a(){
  function b(){
    console.log(myVar);
  }

  var myVar = 2;
  b();
}

var myVar = 1;
a();
b();
> 2
> Uncaught ReferenceError: b is not defined

В приведенном ниже примере показана цепочка областей действия в действии. В переменной среды контекста выполнения функции b отсутствует myVar. Таким образом, он ищет свою внешнюю среду, которая является функцией а. Функция a также не имеет myVar в своей переменной. Таким образом, Механизм поиска выполняет функцию Внешняя среда a, которая является Внешней средой глобального контекста выполнения, и в ней определяется myVar. Следовательно, console.log печатает 1.

function a(){
  function b(){
    console.log(myVar);
  }

  b();
}

var myVar = 1;
a();
> 1

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

0 голосов
/ 01 мая 2018

Подъем - это поведенческая концепция JavaScript.Подъем (скажем, перемещение) - это концепция, которая объясняет, как и где переменные должны быть объявлены.

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

В большинстве случаев мы встречаемся с двумя типами подъема.

1.Переменная декларация подъема

Позволяет понять это по коду.

 a = 5; // Assign 5 to a
 elem = document.getElementById("demo"); // Find an element 
 elem.innerHTML = a;                     // Display a in the element
 var a; // Declare a
  //output-> 5

Здесь объявление переменной a будет размещено сверху невидимым интерпретатором javascript во время компиляции.Таким образом, мы смогли получить значение.Но такой подход объявления переменных не рекомендуется, так как мы должны объявить переменные top уже как этот.

 var a = 5; // Assign and declare 5 to a
 elem = document.getElementById("demo"); // Find an element 
 elem.innerHTML = a;                     // Display a in the element
  // output -> 5

рассмотрим другой пример.

  function foo() {
     console.log(x)
     var x = 1;
 }

фактически интерпретируется так:

  function foo() {
     var x;
     console.log(x)
     x = 1;
  }

В этом случае x будет неопределенным

Не имеет значения, был ли выполнен код, содержащий объявление переменной.Рассмотрим этот пример.

  function foo() {
     if (false) {
         var a = 1;
     }
     return;
     var b = 1;
  }

Эта функция выглядит следующим образом.

  function foo() {
      var a, b;
      if (false) {
        a = 1;
     }
     return;
     b = 1;
  }

В объявлении переменной поднимается только определение переменной, а не присвоение.

Подъем объявления функции

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

 function demo() {
     foo(); // this will give error because it is variable hoisting
     bar(); // "this will run!" as it is function hoisting
     var foo = function () {
         alert("this would not run!!");
     }
     function bar() { 
         alert("this will run!!");
     }
 }
 demo();

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

var a = 1;
function b() {
  a = 10;
  return;
   function a() {}
}
b();
alert(a);

Этот код окажется таким.

var a = 1;                 //defines "a" in global scope
 function b() {  
   var a = function () {}; //defines "a" in local scope 
    a = 10;                 //overwrites local variable "a"
    return;      
 }       
 b();       
 alert(a); 

Функция a () будет иметь локальную область видимости внутри b ().a () будет перемещаться наверх при интерпретации кода с его определением (только в случае поднятия функции), так что теперь у него будет локальная область видимости, и, следовательно, он не будет влиять на глобальную область видимости, имея собственную область видимости внутри функции b ()..

...