Где хранится запись неизменяемой привязки идентификатора в выражении именованной функции в JavaScript?

Недавно я столкнулся с некоторыми интересными фактами о выражениях именованных функций (NFE). Я понимаю, что к имени функции NFE можно получить доступ в теле функции, что делает рекурсию более удобной и спасает нас arguments.callee, И имя функции не доступно вне тела функции. Например,

var foo = function bar() {
    console.log(typeof bar);
}; 

typeof foo; // 'function'
typeof bar; // 'undefined', inaccessible outside the NFE
foo(); // 'function', accessible inside the NFE

Это хорошо документированная функция, и у kangax есть замечательный пост о NFE, где он упоминал об этом явлении. Что меня больше всего удивляет, так это то, что имя функции NFE не может быть связано с другими значениями в теле функции. Например,

(function foo() {
    foo = 5;
    alert(foo);
})(); // will alert function code instead of 5

В приведенном выше примере мы попытались повторно привязать идентификатор foo с другим значением 5, Но это не удалось! И я обратился к ES5 Spec и обнаружил, что запись неизменяемой привязки была создана и добавлена ​​в записи среды лексической среды при создании NFE.

Проблема заключается в том, что когда NFE ссылается на собственное имя функции внутри тела функции, имя разрешается как свободная переменная. В приведенном выше примере foo упоминается внутри NFE, но не является ни формальным параметром, ни локальной переменной этой функции. Таким образом, это свободная переменная, и ее запись привязки может быть разрешена через свойство [[scope]] NFE.

Поэтому учтите это, если у нас есть другой идентификатор с тем же именем во внешней области видимости, то, похоже, существует некоторый конфликт. Например,

var foo = 1;
(function foo() {
    alert(foo);
})(); // will alert function code rather than 1
alert(foo); // 1

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

Итак, мой вопрос заключается в следующем:

  1. Где хранится запись неизменяемой привязки имени функции?
  2. Как получилось название функции foo перевешивать var foo = 1 когда разрешено внутри НФЭ? Хранятся ли их обязательные записи в одной и той же лексической среде? Если так, то как?
  3. Что стоит за феноменом, который называют функцией foo доступен внутри, но невидим снаружи?

Может кто-нибудь пролить свет на это с ES5 spec? Я не нахожу много дискуссий в Интернете.

1 ответ

Решение

Где хранится запись неизменяемой привязки имени функции?

В дополнительной лексической среде запись, которую вы не можете увидеть:-)

Как получилось название функции foo перевешивать var foo = 1 когда разрешено внутри НФЭ?

На самом деле это не так. Вы можете объявить новый местный var foo в объеме функции без каких-либо столкновений, но если вы этого не сделаете, то свободный foo переменная преобразуется в неизменяемую привязку. Это перевешивает глобальный foo переменные выше в цепочке области, однако.

var foo = 1;
(function foo() { "use strict";
    var foo = 2;
    console.log(foo); // 2
}());
(function foo() { "use strict";
    console.log(foo); // function …
    foo = 2; // Error: Invalid assignment in strict mode
}());

Хранятся ли их обязательные записи в одной и той же лексической среде?

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

Это описано в разделе " Определение функции" (§13) спецификации. Хотя шаги для объявления функций и выражений анонимных функций в основном заключаются в том, чтобы "создать новый объект функции с этим телом функции, используя лексическую среду текущего контекста выполнения для Scope ", выражения именованных функций более сложны:

  1. Позволять funcEnv быть результатом звонка NewDeclarativeEnvironment передача в качестве аргумента лексической среды текущего контекста выполнения
  2. Позволять envRec быть funcEnv Экологическая запись.
  3. Позвоните CreateImmutableBinding(N) конкретный метод envRec прохождение Identifier функции в качестве аргумента.
  4. Позволять closure быть результатом создания нового объекта Function […]. Пройти в funcEnv как сфера
  5. Позвоните InitializeImmutableBinding(N,V) конкретный метод envRec прохождение Identifier функции и closure в качестве аргументов.
  6. Вернуть closure,

Он создает дополнительную среду оболочки только для выражения функции. В коде ES6 с областями действия блока:

var x = function foo(){};
// is equivalent to
var x;
{
    const foo = function() {};
    x = foo;
}
// foo is not in scope here

Что стоит за феноменом, который называют функцией foo доступен внутри, но невидим снаружи?

foo неизменяемая привязка создается не в лексической среде текущего контекста выполнения, а в среде-оболочке, которая используется только для замыкания вокруг выражения функции.

Другие вопросы по тегам