w3schools ventajas react functions example javascript ecmascript-6 arrow-functions

ventajas - javascript map



¿Las funciones de flecha de ES6 aún se cierran sobre "esto" incluso si no lo usan? (1)

Mi pregunta es: ¿la función de la flecha aún se une a esto de manera léxica, manteniendo vivo al Foo mientras el otro está vivo, o puede el Foo ser basura recolectada en esta situación?

En lo que concierne a la especificación, la función de flecha tiene una referencia al objeto de entorno donde se creó, y ese objeto de entorno tiene this , y this refiere a la instancia de Foo creada por esa llamada. Por lo tanto, cualquier código que confíe en que Foo no se guarda en la memoria depende de la optimización, no del comportamiento especificado.

En cuanto a la optimización, todo depende de si el motor de JavaScript que está utilizando optimiza los cierres y si puede optimizar el cierre en la situación específica. (Un número de cosas puede evitarlo). La situación es así con las funciones "normales":

function Foo(other) { var t = this; other.callback = function() { }; }

En esa situación, la función se cierra sobre el contexto que contiene t , y así, en teoría, tiene una referencia a t que a su vez mantiene la instancia de Foo en la memoria.

Esa es la teoría, pero en la práctica, un motor JavaScript moderno puede ver que el cierre no lo utiliza y puede optimizarlo, siempre y cuando no presente un efecto secundario observable. Si lo hace y, en caso afirmativo, cuándo, es totalmente dependiente del motor.

Dado que las funciones de las flechas realmente son cierres léxicos, las situaciones son exactamente análogas, por lo que cabe esperar que el motor de JavaScript haga lo mismo: optimícelo a menos que cause un efecto secundario que pueda observarse. Dicho esto, recuerde que las funciones de flecha son muy nuevas , por lo que puede ser que los motores aún no tengan mucha optimización con esto (no hay juego de palabras) .

La versión de V8 en Chrome (estoy usando Chrome 48.0.2564.116 de 64 bits) actualmente parece hacer esto: Ejecuté Chrome en el modo que te permite forzar la recolección de basura ( google-chrome --js-flags="--expose-gc" ) y corrió esto:

"use strict"; function Foo(other) { other.callback = () => this; // <== Note the use of `this` as the return value } let a = []; for (let n = 0; n < 10000; ++n) { a[n] = {}; new Foo(a[n]); } // Let''s keep a Foo just to make it easy to find in the heap snapshot let f = new Foo({}); log("Done, check the heap"); function log(msg) { let p = document.createElement(''p''); p.appendChild(document.createTextNode(msg)); document.body.appendChild(p); }

En Dev Tools, tomar una instantánea del montón muestra las 10,001 instancias esperadas de Foo en la memoria. Luego gc() en la consola (así es como se fuerza la recolección de basura) y tomé otra instantánea del montón. Las 10,001 instancias de Foo todavía estaban allí:

Luego cambié la devolución de llamada para que no hiciera referencia a this :

other.callback = () => { }; // <== No more `this`

"use strict"; function Foo(other) { other.callback = () => {}; // <== No more `this` } let a = []; for (let n = 0; n < 10000; ++n) { a[n] = {}; new Foo(a[n]); } // Let''s keep a Foo just to make it easy to find in the heap snapshot let f = new Foo({}); log("Done, check the heap"); function log(msg) { let p = document.createElement(''p''); p.appendChild(document.createTextNode(msg)); document.body.appendChild(p); }

Y corrió la página de nuevo. Ni siquiera tuve que gc() , solo había una instancia de Foo en la memoria (la que puse allí para que sea fácil de encontrar en la instantánea) cuando el código terminó de ejecutarse:

Me pregunté si era el hecho de que la devolución de llamada estaba completamente vacía lo que permitía la optimización, y me sorprendió gratamente al descubrir que no lo estaba: Chrome se complace en retener partes del cierre mientras abandona this , como se demuestra aquí:

"use strict"; function Foo(other, x) { other.callback = () => x * 2; } let a = []; for (let n = 0; n < 10000; ++n) { a[n] = {}; new Foo(a[n], n); } // Let''s keep a Foo just to make it easy to find in the heap snapshot let f = new Foo({}, 0); document.getElementById("btn-call").onclick = function() { let r = Math.floor(Math.random() * a.length); log(`a[${r}].callback(): ${a[r].callback()}`); }; log("Done, click the button to use the callbacks"); function log(msg) { let p = document.createElement(''p''); p.appendChild(document.createTextNode(msg)); document.body.appendChild(p); }

<input type="button" id="btn-call" value="Call random callback">

A pesar de que las devoluciones de llamada están allí y tienen su referencia a x , Chrome optimiza la instancia de Foo .

Preguntó acerca de las referencias de especificaciones sobre cómo se resuelve esto en las funciones de flecha: El mecanismo se extiende a lo largo de la especificación. Cada environment (como el entorno creado al llamar a una función) tiene una ranura interna [[thisBindingStatus]] , que es "lexical" para las funciones de flecha. Al determinar el valor de this , se utiliza la operación interna ResolveThisBinding , que utiliza la operación interna GetThisEnviroment para encontrar el entorno que tiene definido. Cuando se realiza una llamada de función "normal", BindThisValue se utiliza para vincular this para la llamada de función si el entorno no es un entorno "lexical" . Así que podemos ver que resolver this desde dentro de una función de flecha es como resolver una variable: el entorno actual se comprueba para this enlace y, al no encontrar uno (porque no está vinculado cuando se llama una función de flecha), va al medio ambiente que contiene

Estoy tratando de entender las reglas de cuándo this está ligado de manera léxica en una función de flecha ES6. Veamos primero esto:

function Foo(other) { other.callback = () => { this.bar(); }; this.bar = function() { console.log(''bar called''); }; }

Cuando construyo un new Foo(other) , se establece una devolución de llamada en ese otro objeto. La devolución de llamada es una función de flecha, y la función de this en la flecha está ligada de manera léxica a la instancia de Foo , por lo que no se recolectará la basura de Foo incluso si no mantengo ninguna otra referencia a Foo .

¿Qué pasa si hago esto en su lugar?

function Foo(other) { other.callback = () => { }; }

Ahora configuro la devolución de llamada a nop, y nunca menciono this en ella. Mi pregunta es: ¿la función de la flecha aún se une a this léxica, manteniendo vivo al Foo mientras el other está vivo, o puede el Foo ser basura recolectada en esta situación?