Funciones de flecha de JavaScript: por qué son necesarias, cómo manejarlas, cuándo usarlas y cuándo no

Una de las innovaciones más notables del JavaScript moderno es la aparición de funciones de flecha, que a veces se denominan funciones de flecha "gordas". Al declarar tales funciones, usan una combinación especial de caracteres - => .

Las funciones de flecha tienen dos ventajas principales sobre las funciones tradicionales. La primera es una sintaxis muy conveniente y compacta. El segundo es que el enfoque para trabajar con this en las funciones de flecha parece más intuitivo que en las funciones ordinarias.

imagen

A veces, estas y otras ventajas conducen al hecho de que a la sintaxis de flecha se le da una preferencia incondicional sobre otras formas de declarar funciones. Por ejemplo, la configuración popular de Eslint de Airbnb obliga a que cada vez que se crea una función anónima, dicha función sea como una flecha.

Sin embargo, al igual que otros conceptos y mecanismos utilizados en la programación, las funciones de flecha tienen sus ventajas y desventajas. Su uso puede causar efectos secundarios negativos. Para utilizar las funciones de flecha correctamente, debe conocer los posibles problemas asociados con ellas.

El material, cuya traducción publicamos hoy, se centrará en cómo funcionan las funciones de flecha. Aquí consideraremos situaciones en las que su uso puede mejorar el código, y situaciones en las que no deberían usarse.

Presenta funciones de flecha en JavaScript


Las funciones de flecha en JavaScript son algo así como las funciones lambda en Python y los bloques en Ruby.

Estas son funciones anónimas con una sintaxis especial que toman un número fijo de argumentos y funcionan en el contexto del alcance que los incluye, es decir, en el contexto de la función u otro código en el que se declaran.

Hablemos de esto con más detalle.

▍ Funciones de flecha de sintaxis


Las funciones de flecha se construyen de acuerdo con un esquema único, mientras que la estructura de funciones puede, en casos especiales, simplificarse. La estructura básica de la función de flecha se ve así:

 (argument1, argument2, ... argumentN) => { //   } 

La lista de argumentos de la función está entre paréntesis, seguida de una flecha compuesta por caracteres = y > , y luego aparece el cuerpo de la función entre llaves.

Esto es muy similar a cómo funcionan las funciones ordinarias, las principales diferencias son que la palabra clave de function se omite aquí y se agrega una flecha después de la lista de argumentos.

Sin embargo, en ciertos casos, se pueden declarar funciones de flecha simples utilizando construcciones mucho más compactas.

Considere la sintaxis que se usa si el cuerpo de la función está representado por una sola expresión. Le permite prescindir de las llaves que enmarcan el cuerpo de la función y elimina la necesidad de devolver explícitamente los resultados de evaluar una expresión, ya que este resultado se devolverá automáticamente. Por ejemplo, podría verse así:

 const add = (a, b) => a + b; 

Aquí hay otra variante de la notación abreviada de la función, utilizada cuando la función tiene un solo argumento.

 const getFirst = array => array[0]; 

Como puede ver, los paréntesis que rodean la lista de argumentos se omiten aquí. Además, el cuerpo de la función, que en este ejemplo está representado por un solo comando, también se escribe sin corchetes. Más adelante hablaremos más sobre los beneficios de tales diseños.

▍ Devolver objetos y funciones de flecha de registro corto


Cuando se trabaja con funciones de flecha, también se utilizan algunas construcciones de sintaxis más complejas, que es útil tener en cuenta.

Por ejemplo, intente usar una expresión de una sola línea para regresar de una función literal de objeto. Puede parecer, dado lo que ya sabemos sobre las funciones de flecha, que una declaración de función se verá así:

 (name, description) => {name: name, description: description}; 

El problema con este código es su ambigüedad. A saber, las llaves que queremos usar para describir el objeto literal parecen que estamos tratando de encerrar el cuerpo de una función en ellas.

Para indicarle al sistema que nos referimos al objeto literal, necesitamos encerrarlo entre paréntesis:

 (name, description) => ({name: name, description: description}); 

▍ Funciones de flecha y su contexto de ejecución


A diferencia de otras funciones, las funciones de flecha no tienen su propio contexto de ejecución .

En la práctica, esto significa que heredan las entidades this y los arguments de la función padre.

Por ejemplo, compare las dos funciones presentadas en el siguiente código. Uno de ellos es ordinario, el segundo es la flecha.

 const test = { name: 'test object', createAnonFunction: function() {   return function() {     console.log(this.name);     console.log(arguments);   }; }, createArrowFunction: function() {   return () => {     console.log(this.name);     console.log(arguments);   }; } }; 

Hay un objeto de test con dos métodos. Cada uno de ellos es una función que crea y devuelve una función anónima. La diferencia entre estos métodos es solo que en el primero de ellos se usa la expresión funcional tradicional, y en la segunda función de flecha.

Si experimentamos con este código en la consola, pasando los mismos argumentos a los métodos del objeto, entonces, aunque los métodos son muy similares, obtendremos resultados diferentes:

 > const anon = test.createAnonFunction('hello', 'world'); > const arrow = test.createArrowFunction('hello', 'world'); > anon(); undefined {} > arrow(); test object { '0': 'hello', '1': 'world' } 

Una función anónima tiene su propio contexto, por lo que cuando se llama, cuando se llama a test.name , no se test.name el valor de la propiedad de name del objeto, y cuando llame a arguments , no se mostrará la lista de argumentos de la función que se utilizó para crear y devolver la función bajo investigación.

En el caso de una función de flecha, resulta que su contexto coincide con el contexto de la función que la creó, lo que le da acceso tanto a la lista de argumentos pasados ​​por esta función como a la propiedad de name del objeto cuyo método es esta función.

Situaciones en las que las funciones de flecha mejoran el código


▍Procesando listas de valores


Las funciones lambda tradicionales, así como las funciones de flecha, después de que aparecen en JavaScript, generalmente se usan en situaciones en las que se aplica una determinada función a cada elemento de una lista determinada.

Por ejemplo, si hay una matriz de valores que necesita convertirse utilizando el método de matrices de map , una función de flecha es ideal para describir dicha conversión:

 const words = ['hello', 'WORLD', 'Whatever']; const downcasedWords = words.map(word => word.toLowerCase()); 

Aquí hay un ejemplo extremadamente común del uso similar de las funciones de flecha, que consiste en trabajar con las propiedades de los objetos:

 const names = objects.map(object => object.name); 

Del mismo modo, si en lugar de los bucles tradicionales for usan bucles modernos basados ​​en iterador para forEach bucle, entonces las funciones de flecha usan this entidad principal, hace que su uso sea intuitivo:

 this.examples.forEach(example => { this.runExample(example); }); 

▍ Promesas y cadenas de promesas


Otra situación en la que las funciones de flecha le permiten escribir código más limpio y más comprensible está representada por construcciones de software asincrónicas.

Entonces, las promesas simplifican enormemente el trabajo con código asincrónico. Al mismo tiempo, incluso si prefiere utilizar la construcción asíncrona / espera , no puede prescindir de comprender las promesas , ya que esta construcción se basa en ellas.

Sin embargo, cuando se usan promesas, debe declarar las funciones que se invocan después de completar el código asincrónico o la finalización de la llamada asincrónica a una determinada API.

Este es un lugar ideal para usar las funciones de flecha, especialmente si la función resultante tiene un cierto estado, se refiere a algo en el objeto. Por ejemplo, podría verse así:

 this.doSomethingAsync().then((result) => { this.storeResult(result); }); 

▍ Transformación de objetos


Otro caso de uso común para las funciones de flecha es encapsular transformaciones de objetos.

Por ejemplo, en Vue.js, hay un patrón común para incluir fragmentos de almacenamiento de Vuex directamente en un componente de Vue usando mapState .

Esta operación incluye declaraciones de un conjunto de "convertidores" que seleccionan exactamente lo que se necesita para un componente en particular desde el estado inicial completo.

Estas transformaciones simples son el lugar perfecto para usar las funciones de flecha. Por ejemplo:

 export default { computed: {   ...mapState({     results: state => state.results,     users: state => state.users,   }); } } 

Situaciones en las que no se deben usar las funciones de flecha


▍ Métodos de objeto


Hay una serie de situaciones en las que el uso de funciones de flecha no es una buena idea. Las funciones de flecha, si se usan a la ligera, no solo no ayudan a los programadores, sino que también se convierten en una fuente de problemas.

La primera de estas situaciones es utilizar funciones de flecha como métodos de objeto. El contexto de ejecución y la this , específica de las funciones tradicionales, son importantes aquí.

Hubo un tiempo en que era popular usar una combinación de propiedades de clase y funciones de flecha para crear métodos con "enlace automático", es decir, aquellos que pueden ser utilizados por los controladores de eventos, pero que permanecen vinculados a la clase. Se parecía a esto:

 class Counter { counter = 0; handleClick = () => {   this.counter++; } } 

Usando una construcción similar, incluso si el controlador de eventos handleClick función handleClick , y no en el contexto de una instancia de la clase Counter , esta función tenía acceso a los datos de esta instancia.

Sin embargo, este enfoque tiene muchos inconvenientes a los que se dedica este material .

Aunque el uso de una función de flecha aquí, por supuesto, es una forma conveniente de vincular una función, el comportamiento de esta función en muchos aspectos está lejos de ser intuitivo, interfiriendo con las pruebas y creando problemas en situaciones en las que, por ejemplo, intentan usar el objeto correspondiente como prototipo.

En tales casos, en lugar de las funciones de flecha, use funciones ordinarias y, si es necesario, enlácelas a una instancia del objeto en el constructor:

 class Counter { counter = 0; handleClick() {   this.counter++; } constructor() {   this.handleClick = this.handleClick.bind(this); } } 

▍ Largas cadenas de llamadas


Las funciones de flecha pueden convertirse en una fuente de problemas si se planifican para usarse en muchas combinaciones diferentes, en particular, en largas cadenas de llamadas a funciones.

La razón principal de tales problemas, como cuando se usan funciones anónimas, es que dan resultados extremadamente poco informativos del seguimiento de la pila de llamadas .

Esto no es tan malo si, por ejemplo, solo hay un nivel de anidamiento de llamadas a funciones, por ejemplo, si estamos hablando de la función utilizada en el iterador. Sin embargo, si todas las funciones utilizadas son funciones de flecha declaradas, y estas funciones se llaman entre sí, entonces, si ocurre un error, no será fácil darse cuenta de lo que está sucediendo. Los mensajes de error se verán así:

 {anonymous}() {anonymous}() {anonymous}() {anonymous}() {anonymous}() 

▍ Funciones con contexto dinámico


La última de las situaciones que estamos discutiendo en las que las funciones de flecha pueden convertirse en una fuente de problemas es usarlas donde necesita una dinámica de this enlace.

Si se utilizan funciones de flecha en tales situaciones, entonces la dinámica de this enlace no funcionará. Esta desagradable sorpresa puede llevar a uno a desconcertar las razones de lo que les sucede a aquellos que tienen que trabajar con código en el que las funciones de flecha se usan incorrectamente.

Aquí hay algunas cosas a tener en cuenta al considerar el uso de funciones de flecha:

  • Los controladores de eventos se llaman con this enlace al atributo de evento currentTarget .
  • Si todavía usa jQuery, tenga en cuenta que la mayoría de los métodos jQuery vinculan this al elemento DOM seleccionado.
  • Si usa Vue.js, los métodos y las funciones calculadas generalmente lo vinculan al componente Vue.

Por supuesto, las funciones de flecha pueden usarse intencionalmente para cambiar el comportamiento estándar de los mecanismos de software. Pero, especialmente en casos con jQuery y Vue, esto a menudo entra en conflicto con el funcionamiento normal del sistema, lo que lleva al hecho de que el programador no puede entender por qué algún código que parece completamente normal de repente se niega a funcionar.

Resumen


Las funciones de flecha son una maravillosa característica fresca de JavaScript. Permiten, en muchas situaciones, escribir código más conveniente que antes. Pero, como es el caso con otras características, tienen ventajas y desventajas. Por lo tanto, debe usar las funciones de flecha donde pueden ser útiles, sin considerarlas como un reemplazo completo de las funciones ordinarias.

Estimados lectores! ¿Ha encontrado situaciones en las que el uso de las funciones de flecha conduce a errores, inconvenientes o comportamiento inesperado de los programas?

Source: https://habr.com/ru/post/es428566/


All Articles