В главе 2 мы говорили о "динамической области видимости" как о противопоставлении модели "лексической области видимости", которая и есть то, как работает область видимости в JavaScript (а по факту и во многих других языках).
Мы кратко рассмотрим динамическую область видимости, чтобы крепко усвоить разницу. Но, важнее то, что динамическая область видимости — ближайшая родственница другого механизма (this
) в JavaScript, который мы рассмотрели в книге "this и прототипы объектов".
Как мы уже отметили в главе 2, лексическая область видимости — это набор правил о том, как именно Движок может искать переменную и как он ее может найти. Ключевая характеристика лексической области видимости — она определяется на этапе написания кода (предполагая, что вы не жульничаете с помощью eval()
или with
).
Динамическая область видимости похоже означает, и не зря, что есть модель, при помощи которой область видимости можно определить динамически во время выполнения, вместо статического определения при написании кода. То, что нам нужно. Давайте отразим это в коде:
function foo() {
console.log( a ); // 2
}
function bar() {
var a = 3;
foo();
}
var a = 2;
bar();
Лексическая область видимости хранит информацию о том, что RHS-ссылка на a
в foo()
будет разрешена в глобальную переменную a
, что приведет к тому, что будет выведено значение 2
.
Динамическая область видимости, напротив, не интересуется тем как и где были объявлены функции и области видимости, а скорее интересуется тем откуда они будут вызываться. Иными словами, здесь цепочка областей видимости основана на стеке вызовов, а не на вложенности областей видимости в коде.
Итак, если бы в JavaScript была динамическая область видимости, то когда выполнилась бы foo()
, теоретически нижеприведенный код привел бы к выводу 3
.
function foo() {
console.log( a ); // 3 (not 2!)
}
function bar() {
var a = 3;
foo();
}
var a = 2;
bar();
Как такое может быть? А всё потому, что когда foo()
не может разрешить ссылку на переменную a
, вместо поднятия по цепочке вложенных лексических областей видимости, она взбирается вверх по стеку вызовов, чтобы найти откуда foo()
была вызвана. Поскольку foo()
вызывалась из bar()
, она проверяет переменные в области видимости bar()
и находит там a
со значением 3
.
Странно? Возможно вы так и подумали, на какой-то момент.
Но это всего лишь потому, что вы возможно работали только с (или по меньшей мере глубоко изучили) кодом, который работает c лексической областью видимости. Поэтому то динамическая область видимости и кажется чужой. Если бы вы писали код на языке с динамической областью видимости, для вас всё это показалось бы естественным, а лексическая область видимости показалось бы чудаковатой.
Чтобы внести ясность, в JavaScript нет, на самом деле, динамической области видимости. В нем есть лексическая область видимости. Проще некуда. Но механизм работы this
немного похож на динамическую область видимости.
Ключевое сравнение: лексическая область видимости определяется временем написания кода, тогда как динамическая область видимости (и this
!) определяется во время выполнения. Лексическую область видимости интересует где функция была объявлена, а динамическую — откуда была вызвана функция.
И наконец: this
интересует как была вызвана функция, что показывает как близко связаны механизм this
с идеей динамической области видимости. Чтобы изучить во всех подробностях this
, прочтите книгу "this и прототипы объектов".