Этот код работает всегда, даже в разных браузерах:
function fooCheck() {
alert(internalFoo()); // We are using internalFoo() here...
return internalFoo(); // And here, even though it has not been defined...
function internalFoo() { return true; } //...until here!
}
fooCheck();
Однако я не смог найти ни одной ссылки на то, почему это должно работать. Я впервые увидел это в презентации Джона Ресига, но это было только упоминание. Нет никаких объяснений ни там, ни где-либо еще.
Может ли кто-нибудь просветить меня?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я лишь немного использовал JavaScript. Я не уверен, что это поможет, но это очень похоже на то, о чем вы говорите, и может дать некоторое представление:
http://www.dustindiaz.com/javascript-function-declaration-ambiguity/
Некоторые языки требуют, чтобы идентификаторы были определены перед использованием. Причина этого в том, что компилятор использует один проход исходного кода.
Но если есть несколько проходов (или некоторые проверки откладываются), вы вполне можете жить без этого требования. В этом случае код, вероятно, сначала читается (и интерпретируется), а затем устанавливаются ссылки.
Тело функции «internalFoo» должно куда-то перемещаться во время синтаксического анализа, поэтому, когда код читается (также известный как синтаксический анализ) интерпретатором JS, создается структура данных для функции и присваивается имя.
Только позже, затем запускается код, JavaScript фактически пытается выяснить, существует ли "internalFoo" и что это такое, и можно ли его вызвать и т. д.
Браузер читает ваш HTML от начала до конца и может выполнять его по мере чтения и разбора на исполняемые фрагменты (объявления переменных, определения функций и т. д.). Но в любой момент может использовать только то, что было определено в скрипте до этого момента.
Это отличается от других контекстов программирования, которые обрабатывают (компилируют) весь ваш исходный код, возможно, связывают его вместе с любыми библиотеками, которые вам нужны для разрешения ссылок, и создают исполняемый модуль, с которого начинается выполнение.
Ваш код может ссылаться на именованные объекты (переменные, другие функции и т. д.), Которые определены далее, но вы не можете выполнить ссылочный код, пока не будут доступны все части.
По мере того, как вы познакомитесь с JavaScript, вы начнете понимать, что вам нужно писать в правильной последовательности.
Редакция: Чтобы подтвердить принятый ответ (см. Выше), используйте Firebug, чтобы пройти через раздел сценария на веб-странице. Вы увидите, как он переходит от функции к функции, посещая только первую строку, прежде чем он фактически выполнит какой-либо код.
Объявление function является магическим и вызывает привязку его идентификатора до того, как что-либо в его кодовом блоке * будет выполнено.
Это отличается от присваивания с выражением function, которое вычисляется в обычном порядке сверху вниз.
Если вы изменили пример, чтобы сказать:
var internalFoo = function() { return true; };
это перестанет работать.
Объявление функции синтаксически совершенно отдельно от выражения функции, хотя они выглядят почти одинаково и в некоторых случаях могут быть неоднозначными.
Это задокументировано в Стандарт ECMAScript, раздел 10.1.3. К сожалению, ECMA-262 не очень читаемый документ даже по стандартам!
*: содержащая функция, блок, модуль или скрипт.
Я думаю, это действительно не читается. Я только что прочитал раздел 10.1.3, который вы указали, и не понял, почему положения там вызывают такое поведение. Спасибо за информацию.
@bobince Хорошо, я начал сомневаться в себе, когда не нашел ни единого упоминания термина «подъем» на этой странице. Надеюсь, в этих комментариях достаточно Google Juice ™, чтобы все исправить :)
Это популярная комбинация вопросов и ответов. Рассмотрите возможность обновления, добавив ссылку / отрывок на аннотированную спецификацию ES5. (Что немного доступнее.)
В этой статье есть несколько примеров: JavaScript-Scoping-and-Hoisting
Я обнаружил, что довольно много библиотек используют эту функцию до определения, даже некоторые языки позволяют это официально, например. Haskell. Честно говоря, это может быть неплохо, поскольку в некоторых случаях вы можете писать более выразительно.
По той же причине следующее всегда помещает foo в глобальное пространство имен:
if (test condition) {
var foo;
}
На самом деле, это по совсем другим причинам. Блок if не создает область видимости, а блок function() всегда создает ее. Настоящая причина заключалась в том, что определение глобальных имен javascript происходит на этапе компиляции, так что даже если код не запускается, имя определяется. (Извините, что так долго комментировал)
Это называется ПОДЪЕМ - вызов (вызов) функции до того, как она была определена.
Я хочу написать о двух разных типах функций:
Функции выражения и функции объявления
Функции выражения:
Выражения функций могут храниться в переменной, поэтому им не нужны имена функций. Они также будут называться анонимной функцией (функцией без имени).
Для вызова (вызова) этих функций им всегда нужен имя переменной. Такая функция не будет работать, если она вызывается до того, как она была определена, что означает, что здесь не происходит подъема. Мы всегда должны сначала определять функцию выражения, а затем вызывать ее.
let lastName = function (family) {
console.info("My last name is " + family);
};
let x = lastName("Lopez");
Вот как это можно записать в ECMAScript 6:
lastName = (family) => console.info("My last name is " + family);
x = lastName("Lopez");
Функции объявления:
Функции, объявленные со следующим синтаксисом, не выполняются немедленно. Они «сохраняются для последующего использования» и будут выполнены позже, когда они будут вызваны (вызваны). Этот тип функции работает, если вы вызываете ее ДО или ПОСЛЕ того места, где он был определен. Если вы вызываете функцию объявления до того, как она была определена, подъемник работает правильно.
function Name(name) {
console.info("My cat's name is " + name);
}
Name("Chloe");
Пример подъема:
Name("Chloe");
function Name(name) {
console.info("My cat's name is " + name);
}
let fun = theFunction; fun(); function theFunction() {} также будет работать (узел и браузеры)
Действительно ли подъем «вызывает функцию до ее определения»? Увеличение области действия объявленного имени таким образом, чтобы оно было определено для другого кода в этой области - примерно то, что делает JavaScript, чтобы позволить вам вызывать функцию до ее объявления, и это подъем.
В более новых версиях firefox это не работает, если код находится в try / catch. Смотрите эту скрипку: jsfiddle.net/qzzc1evt