Javascript функция определения объема и подъема
Я только что прочитал замечательную статью Бена Черри о JavaScript Scoping and Hoisting, в которой он приводит следующий пример:
var a = 1;
function b() {
a = 10;
return;
function a() {}
}
b();
alert(a);
Используя приведенный выше код, браузер выдаст предупреждение "1".
Я до сих пор не уверен, почему он возвращает "1". Некоторые из вещей, которые он говорит, приходят на ум, как: Все объявления функций поднимаются наверх. Вы можете охватить переменную с помощью функции. Все еще не щелкает для меня.
14 ответов
Подъем функций означает, что функции перемещаются в верхнюю часть их области видимости. То есть,
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"
Вы должны помнить, что она анализирует всю функцию и разрешает все объявления переменных перед ее выполнением. Так....
function a() {}
действительно становится
var a = function () {}
var a
принудительно помещает его в локальную область видимости, а область видимости переменной распространяется на всю функцию, поэтому глобальная переменная по-прежнему равна 1, потому что вы объявили a в локальную область видимости, сделав ее функцией.
Функция 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
происходит только в локальной области, а не в глобальной переменной.
- объявление функции
function a(){}
поднимается первым и ведет себя какvar a = function () {};
следовательно, в местном масштабеa
создано. - Если у вас есть две переменные с одинаковым именем (одна в глобальной, другая в локальной), локальная переменная всегда будет иметь приоритет над глобальной переменной.
- Когда вы установите
a=10
Вы устанавливаете локальную переменнуюa
не глобальный.
Следовательно, значение глобальной переменной остается тем же, и вы получите, предупрежден 1
Удивительно, но ни один из ответов здесь не упоминает актуальность контекста исполнения в цепочке областей видимости.
Механизм 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. Даже если вы вызываете одну и ту же функцию несколько раз, для каждого вызова она создаст свой собственный контекст выполнения. Таким образом, каждый контекст выполнения будет иметь свою собственную копию переменных в своей среде переменных. Нет разделения переменных.
scpope и закрытие и подъем (var / function)
- scpope: глобальная переменная может быть доступна в любом месте (вся область файла), локальная переменная может быть доступна только локальной областью (область функции / блока)!
Примечание: если локальная переменная не использует ключевые слова var в функции, она станет глобальной переменной!- закрытие: функция внутри другой функции, которая может получить доступ к локальной области видимости (родительская функция) и глобальной области видимости, однако другие не могут получить к ней доступ! если только вы не вернете его как возвращаемое значение!
- Подъем: переместите все объявления / отмены функций / функций в область видимости, затем присвойте значение или ноль!
Примечание: это просто переместить объявление, а не переместить значение!
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
function a() { }
является оператором функции, которая создает a
переменная локальная для b
функция.
Переменные создаются при разборе функции независимо от того, var
или оператор функции выполняется.
a = 10
устанавливает эту локальную переменную.
Подъем - это концепция, созданная для того, чтобы нам было легче ее понять. Что действительно происходит, так это то, что объявления выполняются в первую очередь относительно их областей действия, а назначения будут выполняться после этого (не одновременно).
Когда объявления происходят, var a
, затем function b
и внутри этого b
объем, function a
объявлен
Эта функция 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:
Включают 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: а = 1
Случай 2: а = 10
постулирует
var a
был определен и объявлен лексически в глобальной области видимости.a=10
Этот оператор переназначает значение 10, он лексически находится внутри функции b.
Объяснение обоих случаев
Потому что function definition with name property
А такой же, как 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
в 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
Насколько мне известно, подъем происходит с объявлением переменной и объявлением функции, например:
a = 7;
var a;
console.log(a)
Что происходит внутри движка JavaScript:
var a;
a = 7;
console.log(a);
// 7
Или:
console.log(square(7)); // Output: 49
function square(n) { return n * n; }
Это станет:
function square(n) { return n * n; }
console.log(square(7)); // 49
Но присвоения, такие как присвоение переменной, назначение выражения функции, не будут подняты: Например:
console.log(x);
var x = 7; // undefined
Может получиться так:
var x;
console.log(x); // undefined
x = 7;
ES5: подъем функций и подъем переменных
function hoisting
приоритетgreater
чемvariable hoisting
"use strict";
/**
*
* @author xgqfrms
* @license MIT
* @copyright xgqfrms
* @created 2016-06-01
* @modified
*
* @description function-hoisting.js
* @augments
* @example
* @link
*
*/
(function() {
const log = console.log;
var a = 1;
function b() {
a = 10;
log(`local a`, a)
return;
// function hoisting priority is greater than variable hoisting
function a() {}
}
b();
log(`global a`, a);
// local a 10
// global a 1
})();
что равно
(function() {
const log = console.log;
// define "a" in global scope
var a = 1;
function b() {
// define "a" in local scope
var a ;
// assign function to a
a = function () {};
// overwrites local variable "a"
a = 10;
log(`local a`, a);
return;
}
b();
// log global variable "a"
log(`global a`, a);
// local a 10
// global a 1
})();
причина подъема
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
/**
* scpope & closure & hoisting (var/function)
*
* 1. scpope : the global var can be access in any place(the whole file scope), local var only can be accessed by the local scope(function/block scope)!
* Note: if a local variable not using var keywords in a function, it will become a global variable!
*
* 2. closure : a function inner the other function, which can access local scope(parent function) & global scope, howerver it's vars can't be accessed by others! unless, your return it as return value!
*
* 3. hoisting : move all declare/undeclare vars/function to the scope top, than assign the value or null!
* Note: it just move the declare, not move the value!
*
*/
ES6 let
, const
нет подъема
(() => {
const log = console.log;
log(a)
// Error: Uncaught ReferenceError: Cannot access 'a' before initialization
let a = 1;
})();
(() => {
const log = console.log;
log(b)
// Error: Uncaught ReferenceError: Cannot access 'b' before initialization
const b = 1;
})();
реф.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
Чтобы описать хостинг в javascript одним предложением, переменные и функции поднимаются в верхнюю часть области, в которой они объявлены.
Я предполагаю, что вы новичок, чтобы правильно понять подъем, мы сначала поняли разницу между undefined и ReferenceError.
var v;
console.log(v);
console.log(abc);
/*
The output of the above codes are:
undefined
ReferenceError: abc is not defined*/
что мы видим в приведенном ниже коде? переменная и выражение функции убираются.
<script>
var totalAmo = 8;
var getSum = function(a, b){
return a+b;
}
</script>
но реальная картина с доказательством того, что и переменная, и функция подняты на вершину области:
console.log(totalAmo);
console.log(getSum(8,9));
var totalAmo = 8;
var getSum = function(a, b){
return a+b;
}
console.log(totalAmo);
console.log(getSum(9,7));
Вывод первых двух журналов не определен, а TypeError: getSum не является функцией, потому что и var totalAmo, и getSum находятся в верхней части своей области видимости, как показано ниже.
<script>
var totalAmo;
var getSum;
console.log(totalAmo);
console.log(getSum(8,9));
var totalAmo = 8;
var getSum = function(a, b){
return a+b;
}
console.log(totalAmo);
console.log(getSum(9,7));
</script>
Но для объявления функций целые функции поднимаются наверх своей области видимости.
console.log(getId());
function getId(){
return 739373;
}
/* output: 739373, because the whole function hoisted on the top of the scope.*/
Теперь та же самая логика применяется к тем варибалам, выражениям функций и декларациям функций, объявленным внутри функциональной области. Ключевой момент: они не будут подняты наверх файла;
function functionScope(){
var totalAmo;
var getSum;
console.log(totalAmo);
console.log(getSum(8,9));
var totalAmo = 8;
var getSum = function(a, b){
return a+b;
}
}
Итак, когда вы используете ключевое слово var, переменную и функцию, поднятую наверху области (глобальная область и область функции). Что насчет let и const, const и let по-прежнему осведомлены о глобальной области видимости и области действия, как и var, но переменные const и let также осведомлены о другой области, называемой заблокированной областью. область видимости блока присутствует всякий раз, когда есть блок кода, например цикл for, оператор if else, цикл while и т. д.
Когда мы используем const и let для объявления переменной в этой области видимости блока, объявление переменной будет поднято только вверху того блока, в котором она находится, и не будет поднято вверху родительской функции или вершины глобальная область видимости, которую он поднимает.
function getTotal(){
let total=0;
for(var i = 0; i<10; i++){
let valueToAdd = i;
var multiplier = 2;
total += valueToAdd*multiplier;
}
return total;
}
Переменные в примере abobe будут подняты, как показано ниже
function getTotal(){
let total;
var multiplier;
total = 0;
for(var i = 0; i<10; i++){
let valueToAdd;
valueToAdd = i;
multiplier = 2;
total += valueToAdd*multiplier;
}
return total;
}
Все зависит от области действия переменной "а". Позвольте мне объяснить, создавая области в виде изображений.
Здесь JavaScript создаст 3 области.
я) Глобальный охват. ii) Функция b (). iii) Функция a () область действия.
Это ясно, когда вы вызываете область действия метода alert, принадлежащую Global в это время, поэтому он выберет значение переменной 'a' из глобальной области видимости только 1.
Длинный пост!
Но это очистит воздух!
Java Script работает так, что он включает в себя два этапа:
Компиляция (так сказать) - этот шаг регистрирует переменные и объявления функций и их соответствующие области видимости. Он не включает оценку выражения функции:
var a = function(){}
или переменное выражение (например, присвоение3
вx
в случаеvar x =3;
который является ничем иным, как оценкой части RHS.)Интерпретатор: Это часть исполнения / оценки.
Проверьте вывод кода ниже, чтобы понять:
//b() can be called here!
//c() cannot be called.
console.log("a is " + a);
console.log("b is " + b);
console.log("c is " + c);
var a = 1;
console.log("Now, a is " + a);
var c = function() {};
console.log("Now c is " + c);
function b() {
//cannot write the below line:
//console.log(e);
//since e is not declared.
e = 10; //Java script interpreter after traversing from this function scope chain to global scope, is unable to find this variable and eventually initialises it with value 10 in global scope.
console.log("e is " + e) // works!
console.log("f is " + f);
var f = 7;
console.log("Now f is " + f);
console.log("d is " + d);
return;
function d() {}
}
b();
console.log(a);
Давайте разберемся с этим:
На этапе компиляции 'a' будет зарегистрирован в глобальной области видимости со значением '
undefined
". То же самое касаетсяc
', его значение в этот момент будет'undefined
а неfunction()
". 'b
будет зарегистрирован как функция в глобальном масштабе. внутриb
Сфера,f
'будет зарегистрирован как переменная, которая будет неопределенной в данный момент и функция'd
будет зарегистрирован.Когда работает интерпретатор, объявленные переменные и
function()
(а не выражения) могут быть доступны до того, как интерпретатор достигнет фактической строки выражения. Таким образом, переменные будут напечатаныundefined
'и объявленная анонимная функция может быть вызвана ранее. Однако попытка доступа к необъявленной переменной до инициализации ее выражения приведет к ошибке, подобной следующей:
console.log(e)
e = 3;
Теперь, что происходит, когда у вас есть объявление переменной и функции с одинаковым именем.
Ответ таков: функции всегда поднимаются раньше, и если объявляется одна и та же переменная имени, она обрабатывается как дубликат и игнорируется. Помните, порядок не имеет значения. Функции всегда имеют приоритет. Но на этапе оценки вы можете изменить ссылку на переменную на что угодно (она сохраняет то, что было последним присваиванием). Посмотрите на приведенный ниже код:
var a = 1;
console.log("a is " + a);
function b() {
console.log("a inside the function b is " + a); //interpreter finds 'a' as function() in current scope. No need to go outside the scope to find 'a'.
a = 3; //a changed
console.log("Now a is " + a);
return;
function a() {}
}
var a; //treated as duplicate and ignored.
b();
console.log("a is still " + a + " in global scope"); //This is global scope a.
Подъем в JavaScript означает, что объявления переменных выполняются во всей программе перед выполнением любого кода. Поэтому объявление переменной в любом месте кода эквивалентно объявлению ее в начале.
Вот мое резюме ответа с дополнительной аннотацией и сопутствующей скрипкой, с которой можно поиграть.
// hoisting_example.js
// top of scope ie. global var a = 1
var a = 1;
// new scope due to js' functional (not block) level scope
function b() {
a = 10; // if the function 'a' didn't exist in this scope, global a = 10
return; // the return illustrates that function 'a' is hoisted to top
function a(){}; // 'a' will be hoisted to top as var a = function(){};
}
// exec 'b' and you would expect to see a = 10 in subsequent alert
// but the interpreter acutally 'hoisted' the function 'a' within 'b'
// and in doing so, created a new named variable 'a'
// which is a function within b's scope
b();
// a will alert 1, see comment above
alert(a);
Это происходит потому, что имя переменной совпадает с именем функции, означающим "a". Таким образом, из-за поднятия Javascript он пытается решить конфликт имен, и он вернет a = 1.
Я также был смущен этим, пока не прочитал этот пост в разделе "Подъем JavaScript" http://www.ufthelp.com/2014/11/JavaScript-Hoisting.html
Надеюсь, поможет.
Подъем - это поведенческая концепция 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;
}
В этом случае х будет неопределенным
Не имеет значения, был ли выполнен код, который содержит объявление переменной. Рассмотрим этот пример.
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 (),