Чому я можу використовувати функцію, перш ніж вона буде визначена в JavaScript?


168

Цей код завжди працює навіть у різних браузерах:

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();

Я не зміг знайти жодної посилання на те, чому це має працювати. Я вперше побачив це у презентаційній записці Джона Ресіга, але це було лише згадано. Ніякого пояснення ні з цього питання, ні там.

Може хтось, будь ласка, просвітить мене?


3
У новіших версіях Firefox це не працює, якщо код знаходиться у спробі / уловленні. Дивіться цю скрипку: jsfiddle.net/qzzc1evt
Джошуа Сміт

Відповіді:


217

functionДекларація магії і викликає його ідентифікатор , щоб бути пов'язаним , перш ніж що - або в його код-блок * виконується.

Це відрізняється від призначення із functionвиразом, який оцінюється у звичайному порядку зверху вниз.

Якщо ви змінили приклад, сказати:

var internalFoo = function() { return true; };

воно перестане працювати.

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

Це задокументовано в стандарті ECMAScript , розділ 10.1.3 . На жаль, ECMA-262 не дуже читабельний документ навіть за стандартами-стандартами!

*: функція, що містить блок, модуль або сценарій.


Я думаю, це насправді не читається. Я просто прочитав розділ, який ви вказали 10.1.3, і не зрозумів, чому положення там викликають таку поведінку. Спасибі за інформацію.
Еду Феліпе

2
@bobince Гаразд, я почав сумніватися в собі, коли не зміг знайти жодної згадки про термін "вантажопідйомність" на цій сторінці. Сподіваємось, у цих коментарях вистачає Google Juice ™, щоб все правильно налаштувати :)
Mathias Bynens

2
Це популярне комбо на запитання / відповіді. Розгляньте оновлення за допомогою посилання / уривку до анотованої специфікації ES5. (Що трохи доступніше.)

1
У цій статті є кілька прикладів: JavaScript-Scoping-and-
podizing

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

28

Він називається HOISTING - викликає (викликає) функцію до її визначення.

Два різних типи функцій, про які я хочу написати:

Функції вираження та деклараційні функції

  1. Функції вираження:

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

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

    let lastName = function (family) {
     console.log("My last name is " + family);
    };
    let x = lastName("Lopez");

    Ось як це можна записати в ECMAScript 6:

    lastName = (family) => console.log("My last name is " + family);
    
    x = lastName("Lopez");
  2. Функції декларації:

    Функції, оголошені із наступним синтаксисом, виконуються не відразу. Вони "збережені для подальшого використання" і будуть виконані пізніше, коли їх буде викликано (викликано). Цей тип функції працює, якщо ви називаєте його ПЕРЕД або ПІСЛЯ, де це було визначено. Якщо ви викликаєте функцію декларування до того, як вона буде визначена, Хостинг працює належним чином.

    function Name(name) {
      console.log("My cat's name is " + name);
    }
    Name("Chloe");

    Приклад:

    Name("Chloe");
    function Name(name) {
       console.log("My cat's name is " + name);
    }

let fun = theFunction; fun(); function theFunction() {}також буде працювати (Node і браузери)
Fider

14

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

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

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

Познайомившись з JavaScript, ви зрозумієте свою необхідність писати речі у відповідній послідовності.

Версія: Щоб підтвердити прийняту відповідь (вище), використовуйте Firebug, щоб перейти через розділ сценарію веб-сторінки. Ви побачите, що він пропускає з функції в функцію, відвідуючи лише перший рядок, перш ніж він фактично виконує будь-який код.


3

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

Але якщо є декілька пропусків (або деякі перевірки відкладені), ви можете прекрасно жити без цієї вимоги. У цьому випадку код, ймовірно, спочатку зчитується (та інтерпретується), а потім встановлюється посилання.


2

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

http://www.dustindiaz.com/javascript-function-declaration-ambiguity/


Посилання вже не мертва.
mwclarke

1
Посилання мертва.
Джером Індефенцо

Ось знімок з archive.org. Схоже, автор зняв весь веб-сайт за те, що він застарів вміст, не те, що ця публікація в блозі потрапляє до цієї категорії.
jkmartindale

1

Тіло функції "InternalFoo" потрібно пройти кудись під час розбору, тому коли код читається інтерпретатором JS (він також розбирається), структура даних для функції створюється і присвоюється ім'я.

Лише пізніше, після чого запускається код, JavaScript насправді намагається з'ясувати, чи існує "InternalFoo" і що це таке, і чи можна його називати тощо.


-4

З цієї ж причини fooу глобальному просторі імен завжди розміщуються наступні:

if (test condition) {
    var foo;
}

8
Власне, це з зовсім інших причин. ifБлок не створює сферу, в той час як function()блок завжди створює один. Справжня причина полягала в тому, що визначення глобальних назв JavaScript відбувається на етапі компіляції, так що навіть якщо код не працює, ім'я визначається. (Вибачте, що так довго коментувати)
Edu Felipe
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.