Skip to content

Latest commit

 

History

History
58 lines (37 loc) · 6.09 KB

File metadata and controls

58 lines (37 loc) · 6.09 KB

Вы не знаете JS: Область видимости и замыкания

Приложение A: Динамическая область видимости

В главе 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 и прототипы объектов".