¿Cuáles son la semántica precisa de las funciones a nivel de bloque en ES6?
Estoy tratando de entender las nuevas funciones estandarizadas a nivel de bloque en ES6 leyendo la especificación sin formato. Mi comprensión superficial fue:
- Las declaraciones de funciones a nivel de bloque están permitidas en ES6.
- Suben a la cima del bloque.
- En modo estricto, no son visibles fuera del bloque contenedor.
Sin embargo, esto se complica aún más por el hecho de que parte de esta semántica se especifica como "opcional" y sólo obligatoria para los navegadores web ( Anexo B ). Entonces me gustaría tener la siguiente tabla llena:
| ¿Visible fuera del bloque? | ¿Izado? ¿Hasta qué punto? | ¿"TDZ"? | -------------------------------------------------- -------------------------------------------------- -------------------- | Modo no estricto, sin "extensiones web" | | | | | Modo estricto, sin "extensiones web" | | | | | Modo no estricto, con "extensiones web | | | | | Modo estricto, con "extensiones web" | | | |
Tampoco me queda claro qué significa "modo estricto" en este contexto. Esta distinción parece introducirse en el Anexo B3.3 , como parte de algunos pasos adicionales para la ejecución en tiempo de ejecución de una declaración de función:
1. If strict is false, then
...
Sin embargo, hasta donde puedo ver, strict
se refiere a la [[Strict]]
ranura interna del objeto de función. ¿Esto significa que:
// Non-strict surrounding code
{
function foo() {"use strict";}
}
¿Debería considerarse "modo estricto" en la tabla anterior? Sin embargo, eso contradice mi intuición inicial.
Por favor, tenga en cuenta que lo que más me interesa son las especificaciones de ES6 en sí, independientemente de las inconsistencias reales en la implementación.
Por lo que puedo ver,
strict
se refiere a la[[Strict]]
ranura interna del objeto de función.
No y sí. Se refiere al rigor de la función ( o script ) en la que ocurre el bloque que contiene la declaración de función. No al rigor de la función que se va (o no) a declarar.
Las "extensiones web" sólo se aplican a código descuidado (no estricto), y sólo si la apariencia de la declaración de función es "sensata", es decir, si, por ejemplo, su nombre no choca con un parámetro formal o léxicamente. variable declarada.
Tenga en cuenta que no hay diferencia entre código estricto y descuidado sin la semántica de compatibilidad web. En ES6 puro, solo hay un comportamiento para las declaraciones de funciones en bloques.
Así que básicamente tenemos
| web-compat pure
-----------------+---------------------------------------------
strict mode ES6 | block hoisting block hoisting
sloppy mode ES6 | it's complicated ¹ block hoisting
strict mode ES5 | undefined behavior ² SyntaxError
sloppy mode ES5 | undefined behavior ³ SyntaxError
1: Ver más abajo. Se piden advertencias.
2: Normalmente, se SyntaxError
arroja
a 3: La nota en ES5.1 §12 habla de " variaciones significativas e irreconciliables entre las implementaciones " (como estas ). Se recomiendan advertencias.
Entonces, ¿cómo se comporta una implementación de ES6 con compatibilidad web para una declaración de función en un bloque en una función en modo descuidado con semántica heredada?
En primer lugar, todavía se aplica la semántica pura . Es decir, la declaración de función se eleva a la parte superior del bloque léxico.
Sin embargo, también hay una var
declaración que se eleva a la parte superior de la función adjunta.
Y cuando se evalúa la declaración de función (en el bloque, como si se cumpliera como una declaración), el objeto de función se asigna a esa variable con alcance de función.
Esto se explica mejor mediante el código:
function enclosing(…) {
…
{
…
function compat(…) { … }
…
}
…
}
funciona igual que
function enclosing(…) {
var compat₀ = undefined; // function-scoped
…
{
let compat₁ = function compat(…) { … }; // block-scoped
…
compat₀ = compat₁;
…
}
…
}
Sí, eso es un poco confuso, tener dos enlaces diferentes (indicados con los subíndices 0 y 1) con el mismo nombre. Así que ahora puedo responder sucintamente a tus preguntas:
¿Visible fuera del bloque?
Sí, como un var
. Sin embargo, hay un segundo enlace que sólo es visible dentro del bloque.
¿Izado?
Sí dos veces.
¿Hasta qué punto?
Tanto a la función (aunque inicializada con undefined
) como al bloque (inicializado con el objeto de función).
¿"TDZ"?
No en el sentido de la zona muerta temporal de una variable declarada léxicamente ( let
/ const
/ class
) que genera referencias, no. Pero antes de que se encuentre la declaración de función en la ejecución del cuerpo, la variable con alcance de función está undefined
(especialmente antes del bloque), y también obtendrá una excepción si intenta llamarla.
Solo como referencia: en ES6, el comportamiento descrito anteriormente se especificó solo para bloques en alcances de funciones. Desde ES7, lo mismo se aplica a los bloques dentro eval
y a los ámbitos globales.
No estoy seguro de dónde viene tu confusión. Según 10.2.1, está muy claro qué está o no "en modo estricto". En su muestra, foo
la [[Strict]]
ranura interna estaría true
y estará en modo estricto, pero el bloque que la aloja no. La primera oración (la que usted citó) se relaciona con el bloque de alojamiento, no con el contenido generado dentro de él. El bloque de su fragmento no está en modo estricto y, por lo tanto, esa sección se aplica a él.