Лексическая область действия / замыкания в javaScript
Я понимаю, что функции в 'js' имеют лексическую область видимости (т.е. функции создают свою среду (область видимости), когда они определены, а не когда они выполняются).
function f1() {
var a = 1;
f2();
}
function f2() {
return a;
}
f1(); // a is not defined
Когда я запускаю просто 'f ()', он возвращает внутреннюю функцию. Что я получаю, это то, что делает "возврат"!
function f() {
var b = "barb";
return function() {
return b;
}
}
console.log(b); //ReferenceError: b is not defined
Почему вы получаете 'ReferenceError: b не определено?' Но разве вышеприведенная внутренняя функция не имеет доступа к своему пространству, пространству f () и т. Д. Учитывая, что 'b' возвращается в глобальное пространство, разве не будет работать console.log()?
Однако, когда я назначаю 'f ()' новой переменной и запускаю ее:
var x = f();
x();// "barb"
console.log(b); //ReferenceError: b is not defined
Это возвращает "b", что означает "barb", но когда вы снова запустите console.log(), вы получите "ReferenceError:" b "не определено"; Разве "b" не находится в глобальной области видимости, так как он был возвращен? Так почему же "x()" также не вернул внутреннюю функцию, как "f ()"?
4 ответа
Вы, мой друг, полностью сбиты с толку. Ваше первое утверждение само по себе неверно:
функции создают свою среду (область видимости), когда они определены, а не когда они выполняются
На самом деле все наоборот. Определение функции не создает область видимости. Вызов функции создает область.
Что такое сфера?
Проще говоря, область действия - это продолжительность жизни переменной. Видите ли, каждая переменная рождается, живет и умирает. Начало области отмечает время, когда переменная рождается, а конец области отмечает время, когда она умирает.
В начале есть только одна область (называемая областью программы или глобальной областью действия). Переменные, созданные в этой области, умирают только после завершения программы. Они называются глобальными переменными.
Например, рассмотрим эту программу (это не JavaScript):
x = 10 // global variable x
{ // beginning of a scope
x = 20 // local variable x
print(x) // 20
} // end of the scope
print(x) // 10
Здесь мы создали глобальную переменную под названием x
, Затем мы создали область видимости блока. Внутри этого блока мы создали локальную переменную x
, Поскольку локальные переменные затеняют глобальные переменные, когда мы печатаем x
мы получаем 20
, Вернуться в глобальную сферу, когда мы печатаем x
мы получаем 10
(местный x
сейчас мертв).
Блок Области и Функциональные Области
В настоящее время в программировании существует два основных типа областей действия - области блоков и области функций.
Область в предыдущем примере была областью блока. Это просто блок кода. Отсюда и название. Области применения блоков выполняются немедленно.
Области функций, с другой стороны, являются шаблонами областей блоков. Как следует из названия, область действия функции принадлежит функции. Однако, точнее, это относится к вызову функции. Области действия функции не существуют, пока функция не будет вызвана. Например:
var x = 10
function inc(x) {
print(x + 1);
}
inc(3); // 4
print(x); // 10
inc(7); // 8
Как вы можете видеть каждый раз, когда вызываете функцию, создается новая область. Вот почему вы получаете результаты 4
, 10
а также 8
,
JavaScript имеет только функциональные области. У него нет блочных областей. Следовательно, если вы хотите создать область видимости блока, вам нужно создать функцию и немедленно выполнить ее:
var x = 10; // global variable x
(function () { // beginning of a scope
var x = 20; // local variable x
print(x); // 20
}()); // end of the scope
print(x); // 10
Этот шаблон называется выражением функции, вызываемой немедленно (IIFE).
Лексические Области и Динамические Области
Области действия функций снова могут быть двух типов - лексическими и динамическими. Видите ли, в функции есть два типа переменных:
- Свободные переменные
- Связанные переменные
Переменные, объявленные внутри области видимости, привязаны к этой области. Переменные, не объявленные внутри области, являются свободными. Эти свободные переменные принадлежат какой-то другой области, но какой?
Лексическая сфера
В лексической области видимости переменные должны принадлежать родительской области видимости. Например:
function add(x) { // template of a new scope, x is bound in this scope
return function (y) { // template of a new scope, x is free, y is bound
return x + y; // x resolves to the parent scope
};
}
var add10 = add(10); // create a new scope for x and return a function
print(add10(20)); // create a new scope for y and return x + y
JavaScript, как и большинство языков программирования, имеет лексическую область видимости.
Динамическая сфера
В отличие от лексической области видимости, в динамической области видимости переменные должны принадлежать области вызова (области действия вызывающей функции). Например (это также не JS - у него нет динамических областей):
function add(y) { // template of a new scope, y is bound, x is free
return x + y; // x resolves to the calling scope
}
function add10(y) { // template of a new scope, bind y
var x = 10; // bind x
return add(y); // add x and y
}
print(add10(20)); // calling add10 creates a new scope (the calling scope)
// the x in add resolves to 10 because the x in add10 is 10
Вот и все. Просто верно?
Эта проблема
Проблема с вашей первой программой в том, что JavaScript не имеет динамической области видимости. У него есть только лексическая область видимости. Видишь ошибку?
function f1() {
var a = 1;
f2();
}
function f2() {
return a;
}
f1(); // a is not defined (obviously - f2 can't access the `a` inside f1)
Ваша вторая программа - очень большой беспорядок:
function f() {
var b = "barb";
return function() {
return b;
}
}
console.log(b); //ReferenceError: b is not defined
Вот ошибки:
- Ты никогда не звонил
f
, Отсюда и переменнаяb
никогда не создается. - Даже если ты позвонил
f
переменнаяb
будет местнымf
,
Это то, что вам нужно сделать:
function f() {
var b = "barb";
return function() {
return b;
}
}
var x = f();
console.log(x());
Когда вы звоните x
это возвращается b
, Однако это не делает b
Глобальный. Делать b
Глобальный вам нужно сделать это:
var x = f();
var b = x();
console.log(b);
Надеюсь, что это помогло вам понять объемы и функции.
Вы получите "ReferenceError: b не определено", потому что "b" не определено там, где ваш console.log()
Звонок есть. Внутри этой функции есть буква "b", но не снаружи. Ваше утверждение о том, что "b возвращается в глобальное пространство", неверно.
Когда вы вызываете функцию, возвращаемую вашей функцией "f()", она возвращает копию значения, на которое ссылается эта переменная замыкания "b". В этом случае "b" всегда будет этой строкой, поэтому функция возвращает эту строку. Это не приводит к тому, что символ "b" становится глобальной переменной.
function f1() {
var a = 1;
f2();
}
function f2() {
return a;
}
f1(); // a is not defined
f2(); не знает об a, потому что вы никогда не передавали ему "a" (то есть Scope создаются, когда функции определены). Функция look f2 () смогла бы получить доступ к a, если бы она была определена внутри f1();[Функции могут обращаться к переменным в той же области видимости, в которой они "ОПРЕДЕЛЕНЫ", а НЕ "ВЫЗОВАНЫ"]
function f() { var b = "barb"; return function(){ return b; } } console.log(b);
Прежде всего, вам нужно вызвать f (); после выполнения f (); он вернул бы другую функцию, которая должна быть выполнена. т.е.
var a=f(); a();
это приведет к "barb", в этом случае вы возвращаете функцию, а не var b;
function f() { var b = "barb"; return b; }; console.log(f());
Это напечатало бы бородку на экране
Но разве вышеприведенная внутренняя функция не имеет доступа к своему пространству, пространству f() и т. Д.
Да, это имеет. Доступ к b
переменная и возвращает ее значение из функции.
То, что "б" возвращается в мировое пространство
Нет. Возврат значения из функции не означает "сделать переменную доступной в области действия вызывающего". Вызов функции (с f()
) - это выражение, результатом которого является значение, которое вернула функция (в вашем случае, объект без имени). Это значение может быть назначено где-то x
), к его свойству можно получить доступ или от него можно отказаться.
Переменная b
однако остается частным в области, где это было объявлено. Это не [определяется] определено в области, где вы звоните console.log
Вот почему вы получаете ошибку.
То, что вы хотите, кажется,
var x = f();
var b = x(); // declare new variable b here, assign the returned value
console.log( b ); // logs "barb"