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

Reply via email to