Подтвердить что ты не робот

Порядок функций JavaScript: зачем это важно?

Оригинальный вопрос:

JSHint жалуется, когда мой JavaScript вызывает функцию, определенную далее на странице, чем вызов к ней. Однако моя страница предназначена для игры, и никакие функции не вызываются до тех пор, пока все это не скачано. Итак, почему функции порядка отображаются в моем коде?

EDIT: Я думаю, что нашел ответ.

http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting

Я стонаю внутри. Похоже, мне нужно провести ДРУГОЙ день, переупорядочивая шесть тысяч строк кода. Кривая обучения с javascript совсем не крутая, но она очень loooooong.

4b9b3361

Ответ 1

tl; dr Если вы ничего не назовете, пока все не загрузится, вы должны быть в порядке.


Изменение: обзор, который также охватывает некоторые объявления ES6 (let, const): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Scope_Cheatsheet

Это странное поведение зависит от

  1. Как вы определяете функции и
  2. Когда вы их назовете.

Вот несколько примеров.

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

Объявление только функции объявляется не функцией выражения (назначения).