Да, вы правы. Здесь задействованы три различных области (одна для первого параметра, одна для второго и одна для тела).
Однако после инициализации параметров (в своей области видимости) они копируются в новую лексическую среду (в которой будет выполняться тело) (можно найти в 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
с тем же именем завершится неудачей.