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


106

Оригінальне запитання:

JSHint скаржиться, коли мій JavaScript викликає функцію, визначену далі внизу сторінки, ніж виклик до неї. Однак моя сторінка призначена для гри, і жодні функції не викликаються, поки вся ця штука не завантажиться. Отже, чому функції замовлення відображаються в моєму коді?

EDIT: Я думаю, що я, можливо, знайшов відповідь.

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

Я стогну всередині. Схоже, мені потрібно провести КОЖНИЙ день, переоформляючи шість тисяч рядків коду. Крива навчання з javascript зовсім не крута, але це дуже loooooong.


+1 за відмінні посилання в оновленнях. І я сподіваюся, що переконує вас, що вам не потрібно дійсно повторно замовляти код. :)
awm

Відповіді:


294

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(), що видає помилку.


Вибачте, щоб розкопати це, але чи є перевантаження на зразок Array.prototype.someMethod = function () {}? Здається, я отримую помилки, якщо ці речі знаходяться в кінці мого сценарію.
Край

Як ви впевнені, що все завантажується ? Чи є якась загальна практика?
zwcloud

6

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

Якщо ви використовували синтаксис операторів функцій

function foo(){ ... }

Насправді взагалі немає різниці, де ти оголошуєш функцію (вона завжди поводиться так, ніби декларація знаходиться на початку).

З іншого боку, якщо ваша функція була встановлена ​​як звичайна змінна

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

Ви повинні гарантувати, що не будете викликати його до ініціалізації (це насправді може бути джерелом помилок).


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

Поставте коментар щодо початку файлу

/*globals foo1 foo2 foo3*/

Або ви можете використовувати для цього текстове поле. (Я також думаю, що ви можете передати це в аргументах до внутрішньої функції jslint, якщо зможете втрутитися в неї.)


Дякую. Тож / * глобалі * / лінія буде працювати? Добре - що завгодно, щоб JsHint мені сподобався. Я все ще новачок у JavaScript і отримую незрозумілі паузи під час оновлення сторінки, але жодних повідомлених помилок. Тож я подумав, що рішення полягає в тому, щоб грати за всіма правилами, а потім дивитись, чи все таки відбудеться.
Кріс Толворіті

4

Занадто багато людей, що підштовхують довільні правила щодо того, як слід писати JavaScript. Більшість правил - це сміття.

Піднімання функцій - це особливість JavaScript, оскільки це гарна ідея.

Коли у вас є внутрішня функція, яка часто є корисністю внутрішніх функцій, додавання її на початок зовнішньої функції є прийнятним стилем написання коду, але у нього є недолік, який потрібно прочитати через деталі, щоб дійти до того, що зовнішня функція виконує.

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

Стилю кодування, якого ви можете побачити в дикій природі, вам слід уникати, оскільки він не дає переваг і можливий лише рефрактерний біль:

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

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

Це саме те, що потрібно уникати підйомних функцій. Просто вивчіть мову та використовуйте її сильні сторони.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.