Почему функция, определенная в части выражения "if", не видна снаружи?

if (function f() {}) {
    console.log(f) // Throw an error: f is not defined
}

Почему журнал выдает ошибку, f еще не определен в приведенном выше выражении?


Вы ожидаете, что это эквивалентно:

function f () {}
if (true) {
    console.log(f); // Throw an error: f is not defined
}

Ответы

Ответ 1

Когда вы говорите

function f () {}

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

Но, когда вы используете объявление функции в выражении, оно не будет рассматриваться как объявление функции, а выражение функции и будет оцениваться следующим образом (цитата из ECMA Script 5.1 Стандартная спецификация)

13 Определение функции: семантика

Производство

Выражение функции:

 function Identifier ( FormalParameterListopt ) { FunctionBody }

оценивается следующим образом:

  • Пусть funcEnv является результатом вызова NewDeclarativeEnvironment передачи запущенных контекстов выполнения Лексическая среда как аргумент
  • Пусть envRec является записью среды funcEnvs.
  • Вызовите CreateImmutableBinding (N) конкретный метод envRec, передав значение String идентификатора в качестве аргумента.
  • Пусть замыкание является результатом создания нового объекта Function, указанного в 13.2, с параметрами, указанными FormalParameterListopt и телом, указанными FunctionBody. Перейдите в funcEnv как область действия. Перейдите в true как флаг Strict, если FunctionExpression содержится в строгом коде или если его FunctionBody является строгим кодом.
  • Вызовите InitializeImmutableBinding (N, V) конкретный метод envRec, передающий значение String идентификатора и закрытия в качестве аргументов.
  • Возвратное закрытие.

Итак, когда вы создаете функцию в выражении,

  • будет создан новый контекст среды (см. первый пункт)

  • имя функции будет привязано к вновь созданной среде (см. третий элемент).

  • Тело функции будет использоваться для создания фактического объекта функции (см. четвертый элемент)

  • Созданный фактический объект функции привязан к имени функции во вновь создаваемом контексте (см. пятый элемент)

  • Затем возвращается объект функции.

Речь идет о назначении объекту функции переменной в контексте текущей среды для сохранения функции. В противном случае, когда оценка выражения будет выполнена, вновь созданный контекст среды станет недействительным. Таким образом, функция f не будет видна снаружи.

Ответ 2

Ваше предположение неверно. Эти два фрагмента не эквивалентны. Первое - имя функции function, в котором вы не храните значение (фактический объект функции). Такой объект всегда правдивый - и, следовательно, if выполняется.

Имя функции для этих функций (или, скорее, должно быть) видимо только внутри самой функции (за исключением некоторых ранних версий IE). Итак, поскольку f просто не существует, a ReferenceError бросается, когда вы пытаетесь передать его на console.log.

Ответ 3

Операторы объявления функций определяют функцию в их окружении и даже переносят определение в начало. Это, однако, именованное функциональное выражение. Функция определяется ее именем внутри нее, но не снаружи (кроме старого IE, где это ошибка). Вне его, это просто функция, которая имеет свое имя. Только если оператор начинается с ключевого слова function, он считается оператором функции.

Это работает:

function f(){};
if (f) console.log(f);

Ответ 4

if (function f() {}) {
    console.log(f) // Throw an error: f is not defined
}

Объявление функции рассматривается как выражение функции (например, function() {}), а не оператор функции.

Доказательство:

if (function f(x) {return x}(1)) {
    console.log('foo'); //Will log
}

if (function f(x) {return x}(false)) {
    console.log('foo'); //Will not log
}

Если ваша функция была trully выражением, вы не смогли выполнить ее сразу (без ее обертывания в круглых скобках или префиксации ее с помощью !, чтобы превратить ее в выражение).

function f(x) {return x}(false) //Would yield a syntax error