Порядок функций JavaScript: зачем это важно?
Оригинальный вопрос:
JSHint жалуется, когда мой JavaScript вызывает функцию, определенную далее на странице, чем вызов к ней. Однако моя страница предназначена для игры, и никакие функции не вызываются до тех пор, пока все это не скачано. Итак, почему функции порядка отображаются в моем коде?
EDIT: Я думаю, что нашел ответ.
http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting
Я стонаю внутри. Похоже, мне нужно провести ДРУГОЙ день, переупорядочивая шесть тысяч строк кода. Кривая обучения с javascript совсем не крутая, но она очень loooooong.
Ответы
Ответ 1
tl; dr Если вы ничего не назовете, пока все не загрузится, вы должны быть в порядке.
Изменение: обзор, который также охватывает некоторые объявления ES6 (let
, const
): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Scope_Cheatsheet
Это странное поведение зависит от
- Как вы определяете функции и
- Когда вы их назовете.
Вот несколько примеров.
bar(); //This won't throw an error
function bar() {}
foo(); //This will throw an error
var foo = function() {}
bar();
function bar() {
foo(); //This will throw an error
}
var foo = function() {}
bar();
function bar() {
foo(); //This _won't_ throw an error
}
function foo() {}
function bar() {
foo(); //no error
}
var foo = function() {}
bar();
Это из-за чего-то под названием подъем !
Существует два способа определения функций: Объявление функции и выражение функции. Разница раздражает и минута, поэтому давайте просто скажем эту немного неправильную вещь: если вы пишете ее как function name() {}
, это объявление, и когда вы пишете его как var name = function() {}
(или анонимная функция, назначенная возврату, такие вещи), это выражение функции.
Во-первых, давайте посмотрим, как обрабатываются переменные:
var foo = 42;
//the interpreter turns it into this:
var foo;
foo = 42;
Теперь, как обрабатываются объявления функций:
var foo = 42;
function bar() {}
//turns into
var foo; //Insanity! It now at the top
function bar() {}
foo = 42;
Операторы var
"бросают" создание foo
на самый верх, но пока не присваивают ему значение. Объявление функции происходит следующим образом, и, наконец, значение присваивается foo
.
А как насчет этого?
bar();
var foo = 42;
function bar() {}
//=>
var foo;
function bar() {}
bar();
foo = 42;
Только объявление foo
перемещается в верхнюю часть. Назначение происходит только после того, как сделан вызов в bar
, где он был до того, как все подъемники произошли.
И, наконец, для краткости:
bar();
function bar() {}
//turns to
function bar() {}
bar();
Теперь, как насчет функциональных выражений?
var foo = function() {}
foo();
//=>
var foo;
foo = function() {}
foo();
Как и обычные переменные, первый foo
объявляется в самой высокой точке области, тогда ему присваивается значение.
Посмотрим, почему второй пример вызывает ошибку.
bar();
function bar() {
foo();
}
var foo = function() {}
//=>
var foo;
function bar() {
foo();
}
bar();
foo = function() {}
Как мы видели ранее, только создание foo
поднято, назначение происходит там, где оно появилось в "оригинальном" (не-поднятом) коде. Когда вызывается bar
, перед foo
присваивается значение, поэтому foo === undefined
. Теперь в функциональном теле bar
, как будто вы делаете undefined()
, что вызывает ошибку.
Ответ 2
Основная причина, вероятно, в том, что JSLint делает только один проход в файле, поэтому он не знает, что вы определите такую функцию.
Если вы использовали синтаксис инструкции функций
function foo(){ ... }
Фактически нет никакой разницы, где вы объявляете функцию (она всегда ведет себя так, как если бы объявление было в начале).
С другой стороны, если ваша функция была установлена как регулярная переменная
var foo = function() { ... };
Вы должны гарантировать, что вы не назовете его до инициализации (это может быть источником ошибок).
Поскольку переупорядочение тонны кода является сложным и может быть источником ошибок само по себе, я предлагаю вам искать обходной путь. Я уверен, что вы можете заранее указать имя глобальных переменных JSLint, чтобы он не жаловался на незаявленные вещи.
Поставьте комментарий о начале файла
/*globals foo1 foo2 foo3*/
Или вы можете использовать для этого текстовое поле. (Я также думаю, что вы можете передать это в аргументах внутренней функции jslint, если вы можете вмешиваться в нее.)
Ответ 3
Есть слишком много людей, которые нажимают произвольные правила о том, как писать JavaScript. Большинство правил - полный мусор.
Функция hoisting - это функция JavaScript, потому что это хорошая идея.
Когда у вас есть внутренняя функция, которая часто является полезностью внутренних функций, добавление ее в начало внешней функции является приемлемым стилем написания кода, но у него есть недостаток, который вы должны прочитать в деталях получить то, что делает внешняя функция.
Вы должны придерживаться одного принципа на всей вашей кодовой базе, либо ставить частные функции в первую очередь или в последний раз в своем модуле или функции. JSHint хорош для обеспечения согласованности, но вы должны АБСОЛЮТНО отрегулировать .jshintrc в соответствии с вашими потребностями, НЕ скорректируйте исходный код другим концепциям кодирования других людей.
Один стиль кодирования, который вы можете увидеть в дикой природе, следует избегать, потому что он не дает вам никаких преимуществ и только рефакторинга:
function bigProcess() {
var step1,step2;
step1();
step2();
step1 = function() {...};
step2 = function() {...};
}
Это именно то, что нужно избежать. Просто изучите язык и используйте его сильные стороны.
Ответ 4
Объявление только функции объявляется не функцией выражения (назначения).