Это не проблема области видимости и не проблема закрытия. Проблема заключается в понимании между объявлениями и выражениями.
Код JavaScript, поскольку даже первая версия JavaScript Netscape и первая его копия от Microsoft обрабатываются в два этапа:
Этап 1: компиляция - на этом этапе код компилируется в дерево синтаксиса (и байт-код или двоичный код в зависимости от движка).
Фаза 2: выполнение - затем интерпретируется проанализированный код.
Синтаксис для функции объявления:
function name (arguments) {code}
Аргументы, конечно, необязательны (код тоже необязателен, но какой в этом смысл?).
Но JavaScript также позволяет создавать функции с помощью выражений. Синтаксис для функциональных выражений аналогичен объявлениям функций, за исключением того, что они написаны в контексте выражения. А выражения бывают:
- Все, что находится справа от знака
=
(или :
в литералах объекта).
- Все, что указано в скобках
()
.
- Параметры функций (на самом деле это уже описано в 2).
Выражения в отличие от объявлений обрабатываются на этапе выполнения, а не на этапе компиляции. И от этого имеет значение порядок выражений.
Итак, чтобы уточнить:
// 1
(function() {
setTimeout(someFunction, 10);
var someFunction = function() { alert('here1'); };
})();
Фаза 1: компиляция. Компилятор видит, что переменная someFunction
определена, и создает ее. По умолчанию все созданные переменные имеют значение undefined. Обратите внимание, что компилятор еще не может присвоить значения в этот момент, потому что значениям может потребоваться интерпретатор для выполнения некоторого кода, чтобы вернуть значение для назначения. И на этом этапе мы еще не выполняем код.
Фаза 2: исполнение. Интерпретатор видит, что вы хотите передать переменную someFunction
в setTimeout. Так оно и есть. К сожалению, текущее значение someFunction
не определено.
// 2
(function() {
setTimeout(someFunction, 10);
function someFunction() { alert('here2'); }
})();
Фаза 1: компиляция. Компилятор видит, что вы объявляете функцию с именем someFunction, и поэтому он ее создает.
Этап 2: интерпретатор видит, что вы хотите передать someFunction
в setTimeout. Так оно и есть. Текущее значение someFunction
- это его скомпилированное объявление функции.
// 3
(function() {
setTimeout(function() { someFunction(); }, 10);
var someFunction = function() { alert('here3'); };
})();
Фаза 1: компиляция. Компилятор видит, что вы объявили переменную someFunction
, и создает ее. Как и раньше, его значение не определено.
Фаза 2: исполнение. Интерпретатор передает анонимную функцию в setTimeout для последующего выполнения. В этой функции он видит, что вы используете переменную someFunction
, поэтому он закрывает переменную. На данный момент значение someFunction
все еще не определено. Затем он видит, что вы назначаете функцию someFunction
. На данный момент значение someFunction
больше не является неопределенным. Спустя 1/100 секунды срабатывает setTimeout и вызывается someFunction. Поскольку его значение больше не является неопределенным, он работает.
Случай 4 на самом деле является другой версией случая 2 с добавлением части случая 3. В момент, когда someFunction
передается в setTimeout, он уже существует из-за того, что он объявлен.
Дополнительные пояснения:
Вы можете задаться вопросом, почему setTimeout(someFunction, 10)
не создает замыкание между локальной копией someFunction и копией, переданной в setTimeout. Ответ на этот вопрос заключается в том, что аргументы функции в JavaScript всегда всегда передаются по значению, если они являются числами или строками, или по ссылке для всего остального. Таким образом, setTimeout фактически не получает переданную ему переменную someFunction (что означало бы создание замыкания), а только получает объект, на который ссылается someFunction (который в данном случае является функцией). Это наиболее широко используемый механизм в JavaScript для взлома замыканий (например, в циклах).
person
slebetman
schedule
08.10.2010