Где хранится запись неизменяемой привязки идентификатора в выражении именованной функции в 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
была решена как локальная переменная во внешней области видимости.
Итак, мой вопрос заключается в следующем:
- Где хранится запись неизменяемой привязки имени функции?
- Как получилось название функции
foo
перевешиватьvar foo = 1
когда разрешено внутри НФЭ? Хранятся ли их обязательные записи в одной и той же лексической среде? Если так, то как? - Что стоит за феноменом, который называют функцией
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 ", выражения именованных функций более сложны:
- Позволять
funcEnv
быть результатом звонкаNewDeclarativeEnvironment
передача в качестве аргумента лексической среды текущего контекста выполнения - Позволять
envRec
бытьfuncEnv
Экологическая запись. - Позвоните
CreateImmutableBinding(N)
конкретный методenvRec
прохождениеIdentifier
функции в качестве аргумента. - Позволять
closure
быть результатом создания нового объекта Function […]. Пройти вfuncEnv
как сфера - Позвоните
InitializeImmutableBinding(N,V)
конкретный методenvRec
прохождениеIdentifier
функции иclosure
в качестве аргументов. - Вернуть
closure
,
Он создает дополнительную среду оболочки только для выражения функции. В коде ES6 с областями действия блока:
var x = function foo(){};
// is equivalent to
var x;
{
const foo = function() {};
x = foo;
}
// foo is not in scope here
Что стоит за феноменом, который называют функцией
foo
доступен внутри, но невидим снаружи?
foo
неизменяемая привязка создается не в лексической среде текущего контекста выполнения, а в среде-оболочке, которая используется только для замыкания вокруг выражения функции.