Давайте рассмотрим следующий код:
let num = 1; // задаем значение переменной
function func() {
alert(num); // выводим его на экран
}
func(); // вызываем функцию
Как я уже упоминал ранее, значение переменной не обязательно должно быть перед определением функции, главное, чтобы оно стояло перед ее вызовом:
function func() {
alert(num);
}
let num = 1;
func();
На самом деле это не совсем так. Наша функция
даже до своего вызова знает значение переменной
num
:
let num = 1;
function func() {
alert(num); // функция уже знает, что num = 1
}
Вот более сложный пример:
let num = 1; // функция в этот момент узнает, что num = 1
function func() {
alert(num);
}
num = 2; // функция в этот момент узнает, что num = 2
Добавим вызовы функции:
let num = 1; // функция в этот момент узнает, что num = 1
func(); // выведет 1
function func() {
alert(num);
}
func(); // выведет 1
num = 2; // функция в этот момент узнает, что num = 2
func(); // выведет 2
Еще раз: на самом деле функция знает значения внешних переменных, даже не будучи вызванной.
Лексическое окружение
Все внешние, доступные функции переменные, называются ее лексическим окружением (англ. LexicalEnvironment).
В следующем примере функции доступны две
переменные: num1
и num2
, которые
и являются лексическим окружением нашей функции:
let num1 = 1;
let num2 = 2;
function func() {
// функция знает про переменные num1 и num2
}
Само лексическое окружение представляет собой некий внутренний объект JavaScript, привязанный к нашей функции. В данном случае его можно представить в следующем виде:
{num1: 1, num2: 2}
Значение любой переменной лексического окружения всегда равно текущему значению этой переменной:
let num1 = 1; // окружение {num1: 1}
let num2 = 2; // окружение {num1: 1, num2: 2}
// Поменяем переменную num1:
num1 = 123; // окружение {num1: 123, num2: 2}
function func() {
}
Когда мы пытаемся обратится к какой-либо переменной внутри функции, эта переменная вначале ищется среди локальных переменных функции и, если такой переменной там нет, то ищется в лексическом окружении функции.
Практическое применение
Пусть у нас есть функция, своим результатом возвращающая другую функцию:
function test() {
return function() {
}
}
Если родительская функция имеет какие-либо переменные, то эти переменные будут содержаться в лексическом окружении возвращаемой функции:
function test() {
let num = 1; // переменная родительской функции
return function() {
// лексическое окружение = {num: 1}
}
}
Напишем в коде нашей возвращаемой функции
алерт, выводящий на экран значение переменной
num
:
function test() {
let num = 1;
return function() {
alert(num);
}
}
Давайте теперь вызовем родительскую функцию
test
и результат ее работы запишем
в переменную func
:
function test() {
let num = 1;
return function() {
alert(num);
}
}
let func = test();
В переменную func
запишется возвращаемая
функция. Давайте вызовем нашу функцию - своим
результатом она выведет содержимое переменной
num
:
function test() {
let num = 1;
return function() {
alert(num);
}
}
let func = test();
func(); // выведет 1
Если же просто попытаться вывести переменную
num
вне функции - она будет недоступна:
function test() {
let num = 1;
return function() {
alert(num);
}
}
alert(num); // переменная num тут недоступна
Как вы видите, локальная переменная num
привязалась к лексическому окружению нашей
функции и теперь, вызвав в любом месте кода
эту функцию, мы сможем получить значение
переменной num
, даже если в месте
вызова сама по себе эта переменная и недоступна.
На самом деле аналогичного результата можно
добиться, сделав переменную num
глобальной:
function test() {
return function() {
alert(num);
}
}
let num = 1; // глобальная переменная
let func = test();
func(); // выведет 1
Здесь, однако, будет существенная разница:
в новом варианте переменную num
можно
менять вне функций (так как она глобальная),
а в старом - нет.
Определите, не запуская код, что выведется на экран:
function test() {
let num1 = 1;
let num2 = 2;
return function() {
return num1 + num2;
}
}
let func = test();
alert(func());
Определите, не запуская код, что выведется на экран:
function test() {
let num1 = 1;
let num2 = 2;
return function() {
return num1 + num2;
}
}
alert(test()());
Определите, не запуская код, что выведется на экран:
function test() {
let num1 = 1;
return function() {
return num1 + num2;
}
}
let num2 = 2;
let func = test();
alert(func());
Определите, не запуская код, что выведется на экран:
function test() {
let num = 1;
return function() {
return num;
}
}
let num = 2;
let func = test();
alert(func());