Почему я могу использовать функцию до ее определения в JavaScript? - PullRequest
147 голосов
/ 04 ноября 2008

Этот код всегда работает, даже в разных браузерах:

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

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

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

Ответы [ 7 ]

196 голосов
/ 04 ноября 2008

Объявление function является магическим и вызывает привязку его идентификатора до выполнения чего-либо в его кодовом блоке *.

Это отличается от назначения с выражением function, которое оценивается в обычном порядке сверху вниз.

Если вы изменили пример, чтобы сказать:

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

это перестанет работать.

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

Это задокументировано в стандарте ECMAScript , раздел 10.1.3 . К сожалению, ECMA-262 не очень удобочитаемый документ даже по стандартам!

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

16 голосов
/ 16 февраля 2017

Это называется 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);
    }
    
13 голосов
/ 04 ноября 2008

Браузер читает ваш HTML от начала до конца и может выполнять его, когда он читается и разбирается на исполняемые куски (объявления переменных, определения функций и т. Д.). точка.

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

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

Когда вы ознакомитесь с JavaScript, вы начнете глубоко осознавать свою необходимость писать вещи в правильной последовательности.

Редакция: Чтобы подтвердить принятый ответ (см. Выше), используйте Firebug для перехода через раздел сценариев веб-страницы. Вы увидите, что он переходит от функции к функции, посещая только первую строку, прежде чем он фактически выполнит какой-либо код.

3 голосов
/ 04 ноября 2008

В некоторых языках требуется, чтобы идентификаторы были определены перед использованием. Причина этого заключается в том, что компилятор использует один проход для исходного кода.

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

2 голосов
/ 04 ноября 2008

Я только немного использовал JavaScript. Я не уверен, поможет ли это, но выглядит очень похоже на то, о чем вы говорите, и может дать некоторое представление:

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

0 голосов
/ 04 ноября 2008

По той же причине следующее всегда будет помещать foo в глобальное пространство имен:

if (test condition) {
    var foo;
}
0 голосов
/ 04 ноября 2008

Тело функции «internalFoo» должно быть где-то во время синтаксического анализа, поэтому, когда код читается (или синтаксический анализ) интерпретатором JS, создается структура данных для функции и присваивается имя.

Только позже, затем код запускается, JavaScript фактически пытается выяснить, существует ли «internalFoo» и что это такое, можно ли его вызывать и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...