Це не проблема сфери застосування та не проблема закриття. Проблема полягає в розумінні між деклараціями та виразами .
Код JavaScript, оскільки навіть перша версія JavaScript Netscape та перша його копія від Microsoft, обробляються у два етапи:
Етап 1: компіляція - на цій фазі код компілюється в дерево синтаксису (і байт-код або двійковий файл, залежно від механізму).
Фаза 2: виконання - проаналізований код потім інтерпретується.
Синтаксис для оголошення функції :
function name (arguments) {code}
Аргументи, звичайно, необов’язкові (код також необов’язковий, але який сенс у цьому?).
Але JavaScript також дозволяє створювати функції за допомогою виразів . Синтаксис виразів функцій подібний до оголошень функцій, за винятком того, що вони написані в контексті виразів. І вирази:
- Будь-що праворуч від
=
знака (або :
на літералах об’єктів).
- Будь-що в дужках
()
.
- Параметри функцій (це фактично вже описано в 2).
Вирази, на відміну від оголошень , обробляються на етапі виконання, а не на етапі компіляції. І через це порядок виразів має значення.
Отже, для уточнення:
(function() {
setTimeout(someFunction, 10);
var someFunction = function() { alert('here1'); };
})();
Етап 1: складання. Компілятор бачить, що зміннаsomeFunction
визначена, тому вона її створює. За замовчуванням усі створені змінні мають значення undefined. Зауважте, що на даний момент компілятор не може призначати значення, оскільки значенням може знадобитися інтерпретатор для виконання якогось коду для повернення значення для призначення. І на цьому етапі ми ще не виконуємо код.
Фаза 2: виконання. Інтерпретатор бачить, що ви хочете передати змінну someFunction
setTimeout. І так воно і відбувається. На жаль, поточне значення someFunction
не визначено.
(function() {
setTimeout(someFunction, 10);
function someFunction() { alert('here2'); }
})();
Етап 1: складання. Компілятор бачить, що ви оголошуєте функцію з іменем someFunction, і тому він її створює.
Етап 2: Інтерпретатор бачить, що ви хочете перейти someFunction
до setTimeout. І так воно і відбувається. Поточне значення - someFunction
це його складена декларація функції.
(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 для розриву закриття (наприклад, у циклах).