Порядок функций JavaScript: почему это важно? - PullRequest
100 голосов
/ 30 сентября 2011

Исходный вопрос:

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

РЕДАКТИРОВАТЬ: Я думаю, что, возможно, нашел ответ.

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

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

Ответы [ 4 ]

276 голосов
/ 30 сентября 2011

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's 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(), что выдает ошибку.

6 голосов
/ 30 сентября 2011

Основная причина, вероятно, заключается в том, что JSLint выполняет только один проход файла, поэтому он не знает, будет ли определять такую ​​функцию.

Если вы использовали синтаксис оператора функции

function foo(){ ... }

На самом деле вообще нет разницы, где вы объявляете функцию (она всегда ведет себя так, как если бы объявление было в начале).

С другой стороны, если ваша функция была установлена ​​как обычная переменная

var foo = function() { ... };

Вы должны гарантировать, что не будете вызывать его перед инициализацией (это может быть источником ошибок).


Так как переупорядочение тонн кода является сложным и может само по себе являться источником ошибок, я бы посоветовал вам найти обходной путь. Я почти уверен, что вы можете заранее сообщить JSLint имя глобальных переменных, чтобы он не жаловался на незадекларированные данные.

Оставить комментарий в начале файла

/*globals foo1 foo2 foo3*/

Или вы можете использовать для этого текстовое поле. (Я также думаю, что вы можете передать это в аргументах внутренней функции jslint, если вы можете вмешиваться в это.)

3 голосов
/ 18 апреля 2015

Слишком много людей выдвигают произвольные правила о том, как должен быть написан JavaScript.Большинство правил - полная чушь.

Подъем функций - это функция в JavaScript, потому что это хорошая идея.

Когда у вас есть внутренняя функция, которая часто является утилитой внутренних функций, добавление ее в начало внешней функции является приемлемым стилем написания кода, но у него есть недостаток, который вы должны прочитатьподробности, чтобы понять, что делает внешняя функция.

Вы должны придерживаться одного принципа в вашей кодовой базе, либо ставить частные функции на первое или последнее место в вашем модуле или функции.JSHint хорош для обеспечения согласованности, но вы должны АБСОЛЮТНО корректировать .jshintrc в соответствии с вашими потребностями, а НЕ настраивать ваш исходный код в соответствии с дурацкими концепциями кодирования других людей.избегайте, потому что это не дает вам никаких преимуществ и дает только возможную боль при рефакторинге:

function bigProcess() {
    var step1,step2;
    step1();
    step2();

    step1 = function() {...};
    step2 = function() {...};
}

Это именно то, чего стоит избегать при подъеме функций.Просто выучи язык и используй его сильные стороны.

1 голос
/ 13 декабря 2017

Поднимается только объявление функции, а не выражение функции (присваивание).

...