The Sept 20 TC39 meeting notes says the following was (almost) agreed regarding
function parameter scoping, pending some further investigation.
> Temporarily, this happened:
> Conclusion/Resolution
> - `var` bindings and are in scope within the function
> - cannot use `let` to shadow a parameter
> - defaults can refer to any top level binding
> **Conclusion/Resolution**
> Revisit when data is gathered, re: perf or unexpected behaviours
We also agreed, that repeat use of an identifier as a parameter, for example:
function f(x,x,x,x) {}
is only allowed for legacy compatibility and that it will be an early error if
repeated parameter names are used in a parameter list that contains any of the
new ES6 parameter list syntactic affordances. (Call this Agreement 0)For
example, these would all be errors:
function f(x,x,x,x, ...x) {}
function f(x,x=1) {}
function f( {x,x} ){ };
An implication of the "defaults can rely on any top level binding" point is
that parameter default value initializers can refer to inner functions defined
using function top-level function declarations. For example:
function f(p=computeDefaultValue()) {
function computeDefaultValue() {return Math.random()}
return p;
}
The notes don't literally say this, but it was one of the intentions of that
item that we discussed at the meeting.
This implies that inner function declaration initialization is hoisted ahead of
formal parameter initialization.
This however exposes some strange interactions between ES<=5.1 rules regarding
duplicate parameter names that are the same as declared inner function.
ES5.1 requires that
(function(p) {
return typeof p;
function p() {};
})(1)
returns "function" rather than "number".
To maintain compatibility, this means that if function initialization is
hoisted before parameter initialization, that in cases like the above either
1) initialization of parameters that have the same name as a function
declaration are skipped
2) declared functions that share names with formal parameters must be
reinitialized to their original function value after parameter initialization
The choice of one of the above specification alternatives really doesn't make
any observable difference, as long as default value initializer aren't used.
However, alternative 1 introduces a specification (and perhaps implementation)
complication. Consider:
(function ({a:{b: {p}}}) {
function p() {};
return typeof p;
}) ({a: {b: {p: 1}}});
The spec. complication is that deep within the destructuring algorithm we need
to be able to skip any binding name that is the same as a declared function
name.
Also, it seems highly unlikely that anyone would actually want to give an inner
function the same name as such a interior destructured parameter name. It
almost surely is a bug. There are no backwards compatibility requires on such
destructured parameter names, so:
Proposal #1: Local bindings introduced via formal parameter destructing are
treated as let bindings rather than var binding. They cannot be the same as
top inner function or var declared name. There similarly are no compatibility
issue with the name of the rest parameter, so it also should be treated as a
let binding.
When default value initializers, the difference between approach 1 and 2 above
becomes observable. Consider,
function (p=[ ], q=p.length) {
function p(a,b,c) {};
return q;
}) ();
If approach 1 above is used, the function call will return 3 -- the number of
parameters of the inner function p. If approach 2 is used, the function will
return 0 -- the length of the empty array that provided the default value of
the p parameter.
Another odd situation where the approaches would differ is:
function (a=p, p=1, c=p) {
function p() {};
return a===c;
}) ();
If approach 1 is used, the function will return true. If approach 2 is used
the function will return false.
These are crazy things for anybody to code and it seems a waste to try to
specify rationale runtime behavior for such irrational code. Instead I suggest:
Proposal #2: It is an early error if both any formal parameter is the same
name as an top level inner function declaration and the formal parameter lists
includes a destructuring, default value initializer, or a rest parameter.
Essential the net of Agreement 0 plus Proposals 1 & 2 would be that only
ES<=5.1 style simple formal parameter lists may have repeated parameter names
or parameter names that are the same as top level inner function declarations.
This could be even more cleanly expressed as:
Proposal #3:
a) ES<=5.1 style formal parameters lists introduce var bindings and
follow ES5.1 initialization rules. This permits top level inner functinon and
var declarations that use the same name as a parameter.
b) parameter lists containing any new ES6 syntax introduce let bindings
for the parameters. This prohibits multiple declaration of a formal parameter
name and inner var/function redeclaration of a parameter name.
Bottom line,
I suggest we implement proposal 3, rather than the temporary conclusions that
were discussed at the Sept. meeting.
Allen
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss