Ответ 1
Это не проблема области и проблема с закрытием. Проблема заключается в понимании выражений и .
JavaScript-код, так как даже первая версия JavaScript первой и второй версий Netscape обрабатывается в два этапа:
Этап 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
равно undefined.
// 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
и создает ее. Как и раньше, его значение undefined.
Этап 2: выполнение. Интерпретатор передает анонимную функцию, чтобы setTimeout выполнялся позже. В этой функции он видит, что вы используете переменную someFunction
, чтобы создать закрытие переменной. В этот момент значение someFunction
все еще undefined. Затем он видит, что вы назначаете функцию someFunction
. В этот момент значение someFunction
больше не undefined. 1/100th секунды спустя вызывается триггеры setTimeout и вызывается функция someFunction. Поскольку его значение больше не undefined, оно работает.
Случай 4 - это действительно другая версия случая 2 с небольшим разбросом 3-х случаев. В точке someFunction
передается setTimeout, она уже существует из-за ее объявления.
Дополнительные пояснения:
Вы можете задаться вопросом, почему setTimeout(someFunction, 10)
не создает замыкание между локальной копией someFunction и переданной в setTimeout. Ответ на этот вопрос заключается в том, что аргументы функции в JavaScript всегда всегда передаются по значению, если они являются числами или строками или по ссылке для всего остального. Таким образом, setTimeout фактически не получает переданную ему переменную someFunction (что означало бы создание замыкания), а скорее получает только объект, на который ссылается someFunction (что в данном случае является функцией). Это наиболее широко используемый механизм JavaScript для нарушения замыканий (например, в циклах).