reservada - this javascript
Usando `super` dentro de una función de flecha dentro de una función de flecha dentro de un método (1)
Parece que esto es de hecho un error en V8 (ahora se ha fixed ). Tenga en cuenta que si no hay una función de flecha anidada, funciona bien.
Entonces, si vamos a echar un vistazo a través del texto de especificación literal para ver si se trata de un error, comencemos con la super
palabra clave en sí:
12.3.5.3 Semántica de tiempo de ejecución: MakeSuperPropertyReference (propertyKey, strict)
La operación abstracta MakeSuperPropertyReference con argumentos propertyKey y estrictamente realiza los siguientes pasos:
- Deje que env sea GetThisEnvironment ().
- Si env.HasSuperBinding () es falso, lanza una excepción ReferenceError.
- Sea realThis be env.GetThisBinding ().
- ReturnIfAbrupt (actualThis).
- Deje que baseValue sea env.GetSuperBase ().
- Sea b RequireObjectCoercible (baseValue).
- ReturnIfAbrupt (bv).
- Devuelva un valor de tipo Referencia que sea una súper referencia cuyo valor base sea bv, cuyo nombre de referencia sea propertyKey, cuyo valor sea realThis y cuyo indicador de referencia estricto sea estricto.
Ignoremos la mayoría de las cosas importantes y preocupémonos por GetThisEnvironment ():
8.3.2 GetThisEnvironment ()
La operación abstracta GetThisEnvironment encuentra el Registro de entorno que actualmente proporciona el enlace de la palabra clave. GetThisEnvironment realiza los siguientes pasos:
- Deje que lex sea el entorno léxico del contexto de ejecución.
- Repetir
a. Deje que envRec sea EnvironmentRecord de lex.
segundo. Deje que exista ser envRec.HasThisBinding ().
do. Si existe es verdadero, devuelve envRec.
re. Sea el valor externo de la referencia del entorno externo de lex.
mi. Deje que lex sea exterior.NOTA El bucle en el paso 2 siempre terminará porque la lista de entornos siempre termina con el entorno global que tiene este enlace.
Ahora que sabemos que las funciones de flecha no tienen enlaces a this
, debe omitir el registro de entorno de la función actual y la función que la encierra de inmediato.
Esto se detendrá una vez que alcance las funciones "normales" y continuará para recuperar la referencia al super
objeto como se esperaba, de acuerdo con la especificación.
Allen Wirfs-Brock, editor de proyectos de la especificación ECMAScript, parece confirmar que esto fue intencional en una respuesta en la lista de correo de es-Discuss hace unos años:
super
tiene un ámbito léxico, así como a la función de cierre más cercana que lo define. Todas las formas de definición de funciones, excepto las funciones de flecha, introducenthis
/super
enlace nuevo, por lo que podemos simplemente [decir] quethis
/super
enlace de acuerdo con la definición de función de flecha que no contiene flecha más cercana.
Estoy tratando de averiguar si algún comportamiento que estoy viendo en el Nodo v4.1.1 (V8 v4.5.103.33) con respecto a las funciones de super
y de flecha es un comportamiento específico , y si es así (o si no), dónde está en la specification que dice que debería (o no) funcionar en los diversos casos que tengo.
En resumen: el uso de super
en una función de flecha ( inner
) dentro de otra función de flecha ( outer
) dentro de un método funciona a menos que outer
tenga argumentos o variables referencias inner
, incluso si hay referencias inner
argumentos o variables de method
. Quiero saber qué dice la especificación al respecto: ¿debería funcionar todo el tiempo, incluso cuando el V8 está fallando? Ninguna de las veces? ¿Solo en los casos específicos en los que V8 actualmente lo está dejando funcionar, y no donde no lo está?
Aquí hay un MCVE:
"use strict";
class Parent {
show(msg) {
console.log(`Parent#show: ${msg}`);
}
}
class Child extends Parent {
method(arg) {
let outer = (x) => {
console.log(`outer: x = ${x}`);
let inner = () => {
super.show(`arg = ${arg}, x = ${x}`);
};
inner();
};
outer(42);
}
}
new Child().method("arg");
Eso falla con:
$ node test.js /path/test.js:13 super.show(`arg = ${arg}, x = ${x}`); ^^^^^ SyntaxError: ''super'' keyword unexpected here at outer (/path/test.js:16:13) at Child.method (/path/test.js:18:9) at Object. (/path/test.js:22:13) at Module._compile (module.js:434:26) at Object.Module._extensions..js (module.js:452:10) at Module.load (module.js:355:32) at Function.Module._load (module.js:310:12) at Function.Module.runMain (module.js:475:10) at startup (node.js:117:18) at node.js:951:3
Si eliminas la referencia a x
está en el inner
:
let inner = () => {
super.show(`arg = ${arg}`); // <== removed x from this
};
funciona y produce:
outer: x = 42 Parent#show: arg = arg
Para probarme a mí mismo que el caso de "trabajos" no era que las funciones se estaban optimizando, las devolví del método y las llamé. Aquí está ese caso un poco más complejo (note los comentarios); esta versión funciona :
"use strict";
class Parent2 {
show(msg) {
console.log(`Parent2#show: ${msg}`);
}
}
class Child2 extends Parent2 {
method(arg) {
let flag = Math.random() < 0.5;
console.log(`method called with ${arg}, flag is ${flag}`);
let x = "A"; // **A**
let outer2 = (/*x*/) => { // **B**
//let x = "C"; // **C**
let inner2 = () => {
super.show(`${x}: ${arg} (${flag})`);
};
return inner2;
};
return outer2;
}
}
let o = new Child2().method("arg");
console.log(`type of outer2: ${typeof o}`);
let i = o();
console.log(`type of inner2: ${typeof i}`);
i("B");
Salida:
method called with arg, flag is false type of outer2: function type of inner2: function Parent2#show: A: arg (false)
Pero si comentamos la línea etiquetada A
y eliminamos B
o C
, falla como lo hace el MCVE.
Más notas:
Debo enfatizar que necesitas tener las funciones de flecha anidadas .
outer
no tiene problemas para acceder asuper
. No quiero saturar la pregunta con otro bloque de código grande, pero si agregasuper.show(`outer: arg = ${arg}, x = ${x}`);
en la parte superior deouter
, funciona bien.Como puede ver, el
inner
usa un argumento y una variable delmethod
(bueno, el MCVE solo usa un argumento), y eso está bien, pero tan pronto como elinner
intenta usar un argumento o variable delouter
, las cosas explotan.Babel y Traceur están perfectamente felices de mostrar el caso en el que el V8 no se ejecutará ( here y here ), pero podría ser que ellos estén cometiendo un error y que el V8 haga lo correcto (o, por supuesto, viceversa).
No se relaciona con las cadenas de la plantilla; La versión anterior a MCVE de esto no los usó (y sí usó promesas, que es como terminamos con flechas dentro de flechas).
Solo para enfatizar, la pregunta es cuál es el comportamiento especificado aquí y dónde se especifica en la especificación .
Mi instinto me dice que esto es solo un error de V8, son los primeros días para esto, después de todo, justo. Pero de cualquier manera, solo estoy tratando de averiguar cuál debería ser el comportamiento, lo que dice la especificación. He intentado seguir sus diversas y variadas secciones hablando sobre super
y "objetos base" y eso, y francamente no lo entiendo.