Escopo dos parâmetros de function padrão em javascript

Eu estou jogando com alguns resources do EcmaScript 2015 e devo dizer que a especificação é bastante difícil de entender.

Eu entendo totalmente que esse código deveria gerar algum tipo de erro:

(function(a = b, b = 1) { })(); 

E eu sei que o valor padrão pode usar o escopo externo:

 (function() { let c = 1; return (function(a = c) { return a === 1; })(); })(); 

Mas eu não entendo porque esses exemplos não são bons:

 (function() { let a = 1; (function(a = a) { })(); })(); (function() { let b = 1; (function(a = b, b = 2) { })(); })(); 

Meu Chrome 59.0.3071.115 lança ReferenceError que a variável não está definida.

Parece que o Chrome está fazendo alguma otimização, onde apenas 1 escopo é criado, onde todos os parâmetros são definidos como inacessíveis, e eles são adicionados um por um após a atribuição.

Alguma prova disso poderia ser:

 (function(a = () => b, b = 2) { return a() === 2; })(); 

Isto parece uma oportunidade perdida para o meu gosto e eu estou querendo saber a especificação força a usar apenas 1 escopo aqui ou isso é apenas detalhes de implementação da v8.

Alguém poderia, por favor, apontar-me para colocar na especificação que poderia esclarecer isso?

Eu não entendo porque esses exemplos não são bons

Porque os inicializadores padrão não são avaliados no escopo pai, mas sim dentro do escopo da function. Os próprios parâmetros já estão no escopo, para que você possa fazer algo como

 (function(a = 2, b = a) { console.log(b); }()); 

Alguém poderia, por favor, apontar-me para colocar na especificação que poderia esclarecer isso?

A seção relevante é §9.2.12 FunctionDeclarationInstantiation .

Devo dizer que a especificação é bastante difícil de entender.

Sim, é, embora seja escrito para implementadores de motores e não para programadores. No entanto, a nota explicativa basicamente confirma sua compreensão da otimização

Se os parâmetros formais da function não incluírem nenhum inicializador de valor padrão, as declarações do corpo serão instanciadas no mesmo Registro de Ambiente que os parâmetros. Se existirem inicializadores de parâmetro de valor padrão, um segundo Registro de Ambiente será criado para as declarações de corpo.

Seus exemplos basicamente desugar para

 (function() { let a = arguments[0] !== undefined ? arguments[0] : b, // ^ clearly a ReferenceError b = arguments[1] !== undefined ? arguments[1] : 1; { } })(); (function() { let c = 1; return (function() { let a = arguments[0] !== undefined ? arguments[0] : c; // ^ works as you'd think { return a === 1; } })(); })(); (function() { let a = 1; (function() { let a = arguments[0] !== undefined ? arguments[0] : a; // ^ again clearly a ReferenceError { } })(); })(); (function() { let b = 1; (function() { let a = arguments[0] !== undefined ? arguments[0] : b, // ^ still a ReferenceError b = arguments[1] !== undefined ? arguments[1] : 2; { } })(); })(); (function() { let a = arguments[0] !== undefined ? arguments[0] : () => b, // ^ works indeed b = arguments[1] !== undefined ? arguments[1] : 2; { return a() === 2; } })();