6
votes

The following code always prints the argument passed in to parameter a, regardless of the presence of a variable with the same name.

Presumably because parameter identifiers are bound separately to variables in scope. Where are they positioned? Are they in the lexical environment?

function foo(a, b = () => a) {
  var a = 1
  console.log(b())
}
foo() // undefined
foo(2) // 2

Is it that var declarations end up in the special VariableEnvironment, while parameters are positioned in the LexicalEnvironment? And let and const avoid conflict by making redefinition an early error?

Relevant also:

1
The parameter list has a scope of its own, as I understand it. The closure for that => function has the parameter a in scope.Pointy
I think it's this part of the static semantics. The "bound names" from the list of parameters is added to the set for the next parameter in the formal parameter list. I'm not 100% positive that I'm looking at the right thing however because I'm only halfway into a cup of coffee.Pointy
Of course things get wildly more complicated when you consider destructuring syntax in the parameter list, but it's almost like there's a new lexical scope left-to-right for each binding position in the formal parameter list, and the function body scope is another lexical layer.Pointy
Sometimes it helps to confirm the expected behavior of various semantics interacting together by just putting it into babel and looking at ES5 output which is usually easier to reason about. But to explain my earlier comment, using let instead of var generates a SyntaxError, which I find much preferable to the var above implicitly shadowing the parameter list's environment record.Patrick Roberts
@52d6c6af these semantics don't really correlate one-to-one with observable objects in the language. No, I don't believe it's related to the arguments object, since the behavior is identical when changing foo to an arrow function.Patrick Roberts

1 Answers

2
votes

In the event that any default values are present, a separate environment record is created for parameters.

The semantics of functions declared in this position are such that this environment record defines their local scope. A note in the spec (see clause 28) says:

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.

More from the spec:

When an execution context is established for evaluating an ECMAScript function a new function Environment Record is created and bindings for each formal parameter are instantiated in that Environment Record. Each declaration in the function body is also instantiated. If the function's formal parameters do not include any default value initializers then the body declarations are instantiated in the same Environment Record as the parameters. If default value parameter initializers exist, a second Environment Record is created for the body declarations. Formal parameters and functions are initialized as part of FunctionDeclarationInstantiation. All other bindings are initialized during evaluation of the function body.

In the absence of default arguments, therefore, I deduce that one of the pre-existing lexical environments (VariableEnvironment or LexicalEnvironment) is used for parameter bindings. Maybe.