¿Qué son las funciones de flecha de ES6, cómo funcionan?

Tengo curiosidad acerca de las funciones de flecha ES6 (funciones de flecha de grasa). ¿Son simplemente azúcar sintáctico derivado de CoffeeScript, o hay más en ellos de lo que parece?

ES6 funciones de flecha en profundidad

Una de las características más bonitas de ES6, podría ganar fácilmente un concurso de belleza, si se realizara un concurso de este tipo. Lo que mucha gente no sabe es que la función de flecha no es simplemente una forma de azúcar sintáctica que podemos usar en lugar de la callback regular. Como me gusta explicárselo a las personas que asisten a mis entrenamientos / talleres, las funciones de flecha son this -sin arguments , new.target , new.target y super . Pasemos ahora a la syntax más corta y profundicemos en los aspectos específicos de la función de flecha.

Ligado a este léxico

Anteriormente, las funciones regulares tendrían su valor establecido en el objeto global si se utilizaban como devoluciones de llamada, en un nuevo objeto en caso de que se les llamara con el new operador o, en el caso de bibliotecas como jQuery, se establecerían en el Objeto que desencadenó un evento en caso de controladores de eventos, o el elemento actual en una iteración de $.each situación resultó muy confusa incluso para desarrolladores con experiencia. Digamos que tienes un trozo de código como el de abajo.

 var obj = { nameValue: 'default', initializeHandlers: function() { var nameInput = document.querySelector('#name'); nameInput.addEventListener('blur', function(event) { this.nameValue = event.target.value; }); } }; obj.initializeHandlers(); 

El problema es que this dentro del controlador de eventos de blur se establece en el objeto global en lugar de obj. En modo estricto – 'use strict'; – corre el riesgo de romper su aplicación porque está configurado en undefined . Para evitar este problema, tenemos dos opciones:

  • Convierta el controlador de eventos en una función vinculada al ámbito externo, utilizando Function.prototype.bind
  • Usa la var self = this; sucia var self = this; expresión en la función initializeHandlers (veo esto como un hack)

Ambas opciones se ilustran a continuación.

 [...] initializeHandlers: function() { var nameInput = document.querySelector('#name'); // more elegant but we can do better var blurHandler = function(event) { this.nameValue = event.target.value; }.bind(this) nameInput.addEventListener('blur', blurHandler); } [...] [...] initializeHandlers: function() { var nameInput = document.querySelector('#name'); // ugly and error-prone var self = this; nameInput.addEventListener('blur', function(event) { self.nameValue = event.target.value; }); } [...] 

Por otro lado, las funciones de flecha no tienen contexto interno. Heredan su contexto del ámbito exterior. Veamos cómo las funciones de flecha resuelven este problema.

 const obj = { nameValue: 'default', initializeHandlers: function() { const nameInput = document.querySelector('#name'); nameInput.addEventListener('blur', (event) => { // this references obj instead of the global object this.nameValue = event.target.value; }); } }; 

En nuestra nueva implementación, this es una referencia al objeto obj y no se pierde debido al anidamiento.

Argumentos léxicos

¿Alguna vez ha intentado acceder al objeto de arguments dentro de una función de flecha? Lo hice, y desperdicié 3 horas seguidas intentando averiguar por qué obtengo los argumentos de la función externa en lugar de los de las funciones de flecha. Afortunadamente, MDN existe y, según las buenas prácticas, verifica la documentación al final, cuando se sienta en un rincón, las rodillas apoyadas en el pecho, meciéndose y repitiéndose: “¡Debería haber sido un carpintero!” Las funciones de flecha no exponen un objeto de arguments . Si intenta acceder a él, obtendrá los argumentos de la función que lo rodea. En nuestro caso, dado el hecho de que la función externa también es una función de flecha, y no tenemos más funciones en la cadena, obtendremos un ReferenceError .

 const variadicAdder = (x) => { return () => { let args = Array.prototype.slice.call(arguments, 0); return args.reduce((accumulator, current) => { return accumulator + current; }, x); } } const variadicAdderOf5 = variadicAdder(5); console.log(variadicAdderOf5(10, 11, 12)); // ReferenceError: arguments is not defined 

No hay solución aquí, ya que no hay nada roto. Lo que podemos hacer es devolver una función simple, en lugar de una flecha, desde nuestro variadicAdder() . Esto nos dará la oportunidad de acceder al objeto de arguments sin ningún problema. El código actualizado se verá como el de abajo con la única diferencia de que realmente funcionará y no generará un error.

 const variadicAdder = (x) => { return function() { let args = Array.prototype.slice.call(arguments, 0); return args.reduce((accumulator, current) => { return accumulator + current; }, x); } } const variadicAdderOf5 = variadicAdder(5); console.log(variadicAdderOf5(10, 11, 12)); // 38 

Para obtener más información sobre Array.prototype.reduce , diríjase a Mozilla Developer Network .

Otras características

Como mencioné en la sección de introducción de este artículo, las funciones de flecha tienen varias características más además del contexto y los argumentos. Lo primero que me gustaría mencionar es que no puede usar el new operador con las funciones de flecha. Como una implicación directa, las funciones de flecha tampoco tienen super() . Fragmentos como el de abajo simplemente lanzan un TypeError .

 const Person = (name) => { this.name = name; }; let p = new Person('John'); // TypeError: Person is not a constructor 

La tercera característica, que también es una implicación directa de la incapacidad de usar el new operador, es el hecho de que las funciones de flecha no tienen new.target . En pocas palabras, new.target permite detectar si una función ha sido llamada como un constructor o no. Las funciones de flecha, heredan new.target de su ámbito de aplicación. Si el ámbito externo es una función, y se llama como un constructor (por ejemplo, new Person('Adrian'); ), new.target apuntará a la función externa. La Red de Desarrolladores de Mozilla ofrece una explicación detallada sobre new.target y lo invito a que la revise.

Este artículo también se publica en mi blog, aquí: / es6-arrow-functions-en-profundidad /