Да, ты прав. Здесь задействованы три разных области видимости (одна для первого параметра, одна для второго и одна для тела).
Однако после того, как параметры были инициализированы (в их собственной области видимости), они копируются в новую лексическую среду (в которой затем будет выполняться тело) (можно найти в 9.2.15 спецификации).
Это означает, что имена параметров существуют не только в области действия параметра, но и в области действия, в которой оценивается тело, поэтому использование одного и того же имени внутри тела является повторным объявлением переменной, что приводит к ошибке (с let
/ const
).
Вот пошаговое руководство по спецификации:
Когда функция анализируется, она создает объект функции, который содержит некоторые внутренние свойства:
[[Environment]]: это ссылка на внешнюю область, так что вы можете получить доступ к переменным внешней области внутри функции (это также вызывает поведение закрытия, [[Environment]] может ссылаться на среду, которая больше не активна) .
[[FormalParameters]]: проанализированный код параметров.
[[ECMAScriptCode]]: код тела функции.
Теперь, когда вы вызываете функцию (9.2.1 [[Call]]), она выделяет запись среды в стеке вызовов, а затем
Let result be OrdinaryCallEvaluateBody(F, argumentsList).
вызвать функцию. Вот тут-то и появляется 9.2.15. Во-первых, он объявит все параметры в окружении тела функции:
[Initialize local helper variables]
21. For each String paramName in parameterNames, do
i. Perform ! envRec.CreateMutableBinding(paramName, false).
[Rules for initializing the special "arguments" variable]
Затем он инициализирует все параметры. Параметры действительно сложные, потому что есть и остальные параметры. Поэтому аргументы должны быть повторены, чтобы превратить их в массивы:
24. Let iteratorRecord be CreateListIteratorRecord(argumentsList)
25. If hasDuplicates is true, then
a. Perform ? IteratorBindingInitialization for formals with iteratorRecord and undefined as arguments.
26. Else,
a. Perform ? IteratorBindingInitialization for formals with iteratorRecord and env as arguments.
Теперь IteratorBindingInitialization
определено в 13.3.3.8:
В основном он оценивает значение по умолчанию и инициализирует привязку в текущей среде.
Когда все параметры инициализированы, можно подготовить среду тела функций. Как упоминалось в процитированном вами комментарии, если нет инициализаторов параметров, новая среда не требуется. Однако если где-то есть значение по умолчанию, будет создана новая среда.
27. If hasParameterExpressions is false, then
[...]
28. Else,
a. NOTE: A separate Environment Record is needed
to ensure that closures created by expressions in the
formal parameter list do not have visibility of
declarations in the function body.
Затем создается «новая область», в которой оценивается тело:
b. Let varEnv be NewDeclarativeEnvironment(env).
c. Let varEnvRec be varEnv's EnvironmentRecord.
d. Set the VariableEnvironment of calleeContext to varEnv.
Затем все переменные и параметры функции объявляются и инициализируются в этой области, что означает, что все параметры копируются:
f. For each n in varNames, do
2. Perform ! varEnvRec.CreateMutableBinding(n, false).
3. If n is [a regular variable declared with "var"], let
initialValue be undefined.
4. Else, [if it is a parameter]
a. Let initialValue be ! envRec.GetBindingValue(n, false)
5. Call varEnvRec.InitializeBinding(n, initialValue).
[Other rules for functions in strict mode]
31. [varEnv gets renamed to lexEnv for some reason]
После этого все внутренние переменные с let
/ const
объявляются (но не инициализируются, они будут инициализированы при достижении let
).
34. Let lexDeclarations be the LexicallyScopedDeclarations of code.
35. For each element d in lexDeclarations, do
a. NOTE: A lexically declared name cannot be the
same as a function/generator declaration, formal
parameter, or a var name. Lexically declared
names are only instantiated here but not initialized.
b. For each element dn of the BoundNames of d, do
i. If IsConstantDeclaration of d is true, then
1. Perform ! lexEnvRec.CreateImmutableBinding(dn, true).
ii. Else,
1. Perform ! lexEnvRec.CreateMutableBinding(dn, false).
Как видите, здесь вызывается CreateMutableBinding
, и, как указано в 8.1.1.1.2...
Конкретный метод записи среды CreateMutableBinding для декларативных записей среды создает новую изменяемую привязку для неинициализированного имени N. Привязка не должна уже существовать в этой записи среды.
Так как параметр уже был скопирован в текущую среду, вызов CreateMutableBinding
с тем же именем не удастся.
person
Jonas Wilms
schedule
25.05.2019