javascript sintaxis ¿Cuándo debo usar las funciones de flecha en ECMAScript 6?




sintaxis funcion flecha (6)

La pregunta está dirigida a las personas que han pensado en el estilo de código en el contexto del próximo ECMAScript 6 (Harmony) y que ya han trabajado con el idioma.

Con () => {} y function () {} estamos obteniendo dos formas muy similares de escribir funciones en ES6. En otros idiomas, las funciones lambda a menudo se distinguen por ser anónimas, pero en ECMAScript cualquier función puede ser anónima. Cada uno de los dos tipos tiene dominios de uso únicos (es decir, cuando es necesario vincularlo explícitamente o no vincularlo explícitamente). Entre esos dominios hay una gran cantidad de casos en los que cualquier notación servirá.

Las funciones de flecha en ES6 tienen al menos dos limitaciones:

  • No trabajar con new
  • Se corrigió this límite de alcance en la inicialización.

Dejando de lado estas dos limitaciones, las funciones de flecha podrían, en teoría, reemplazar las funciones regulares en casi cualquier lugar. ¿Cuál es el enfoque correcto usándolos en la práctica? Deben usarse las funciones de flecha, por ejemplo:

  • "en todas partes funcionan", es decir, en todas partes, una función no tiene que ser agnóstica acerca de this variable y no estamos creando un objeto.
  • solo "donde sea que se necesiten", es decir, escuchas de eventos, tiempos de espera, que deben estar vinculados a un cierto alcance
  • con funciones 'cortas' pero no con funciones 'largas'
  • Solo con funciones que no contengan otra función de flecha.

Lo que estoy buscando es una guía para seleccionar la notación de función adecuada en la versión futura de ECMAScript. La directriz deberá ser clara, para que pueda ser enseñada a los desarrolladores en un equipo, y ser coherente para que no requiera refactorización constante de una notación de función a otra.


Las funciones de flecha se crearon para simplificar el scope función y resolver this palabra clave haciéndola más simple. Utilizan la sintaxis => , que parece una flecha.

Nota: No reemplaza las funciones existentes. Si reemplaza cada sintaxis de funciones con funciones de flecha, no funcionará en todos los casos.

Veamos la sintaxis de ES5 existente. Si this palabra clave estuviera dentro del método de un objeto (una función que pertenece a un objeto), ¿a qué se referiría?

var Actor = {
  name: 'RajiniKanth',
  getName: function() {
     console.log(this.name);
  }
};
Actor.getName();

El fragmento anterior se referiría a un object e imprimiría el nombre "RajiniKanth" . Exploremos el siguiente fragmento y veamos lo que esto indicaría aquí.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach(function(movie) {
     alert(this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

Ahora, ¿qué pasaría si this palabra clave estuviera dentro de method's function del method's function ?

Aquí, esto se referiría a un window object que a la inner function ya que está fuera de scope . Debido a que this , siempre hace referencia al propietario de la función en la que se encuentra, en este caso, ya que ahora está fuera del alcance, la ventana / objeto global.

Cuando está dentro del método de un object , el propietario de la function es el objeto. Por lo tanto, esta palabra clave está vinculada al objeto. Sin embargo, cuando está dentro de una función, ya sea independiente o dentro de otro método, siempre se referirá a la window/global objeto window/global .

var fn = function(){
  alert(this);
}

fn(); // [object Window]

Hay formas de resolver este problema en nuestro propio ES5 , veamos esto antes de sumergirnos en las funciones de flecha de ES6 sobre cómo resolverlo.

Normalmente, crearías una variable fuera de la función interna del método. Ahora el método 'forEach' obtiene acceso a this y, por lo tanto, a las propiedades object's y sus valores.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   var _this = this;
   this.movies.forEach(function(movie) {
     alert(_this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

usando bind para adjuntar la palabra clave this que hace referencia al método a la method's inner function .

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach(function(movie) {
     alert(_this.name + " has acted in " + movie);
   }).bind(this);
  }
};

Actor.showMovies();

Ahora, con la función de flecha ES6 , podemos tratar el problema del lexical scoping de una manera más simple.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  showMovies: function() {
   this.movies.forEach((movie) => {
     alert(this.name + " has acted in " + movie);
   });
  }
};

Actor.showMovies();

Arrow functions son más como sentencias de función, excepto que bind esto al parent scope . Si la arrow function is in top scope , this argumento se referirá a la window/global scope , mientras que una función de flecha dentro de una función normal tendrá este argumento igual que su función externa.

Con arrow funciones de arrow , this está vinculado al scope de scope en el momento de la creación y no se puede cambiar. El nuevo operador, enlazar, llamar y aplicar no tiene efecto en esto.

var asyncFunction = (param, callback) => {
  window.setTimeout(() => {
  callback(param);
  }, 1);
};

// With a traditional function if we don't control
// the context then can we lose control of `this`.
var o = {
  doSomething: function () {
  // Here we pass `o` into the async function,
  // expecting it back as `param`
  asyncFunction(o, function (param) {
  // We made a mistake of thinking `this` is
  // the instance of `o`.
  console.log('param === this?', param === this);
  });
  }
};

o.doSomething(); // param === this? false

En el ejemplo anterior, perdimos el control de esto. Podemos resolver el ejemplo anterior usando una referencia variable de this o usando bind . Con ES6, se vuelve más fácil administrar this ya que está vinculado al lexical scoping .

var asyncFunction = (param, callback) => {
  window.setTimeout(() => {
  callback(param);
  }, 1);
};

var o = {
  doSomething: function () {
  // Here we pass `o` into the async function,
  // expecting it back as `param`.
  //
  // Because this arrow function is created within
  // the scope of `doSomething` it is bound to this
  // lexical scope.
  asyncFunction(o, (param) => {
  console.log('param === this?', param === this);
  });
  }
};

o.doSomething(); // param === this? true

Cuando no a las funciones de flecha

Dentro de un objeto literal.

var Actor = {
  name: 'RajiniKanth',
  movies: ['Kabali', 'Sivaji', 'Baba'],
  getName: () => {
     alert(this.name);
  }
};

Actor.getName();

Actor.getName se define con una función de flecha, pero en la invocación alerta undefined porque this.name undefined está undefined ya que el contexto permanece en la window .

Ocurre porque la función de flecha enlaza el contexto de manera léxica con el window object la window object ... es decir, el alcance externo. Ejecutar this.name es equivalente a window.name , que no está definido.

Prototipo de objeto

La misma regla se aplica al definir métodos en un prototype object . En lugar de usar una función de flecha para definir el método sayCatName, que trae una context window incorrecta:

function Actor(name) {
  this.name = name;
}
Actor.prototype.getName = () => {
  console.log(this === window); // => true
  return this.name;
};
var act = new Actor('RajiniKanth');
act.getName(); // => undefined

Constructores invocadores

this en una invocación de construcción es el objeto recién creado. Al ejecutar un nuevo Fn (), el contexto del constructor Fn es un objeto nuevo: this instanceof Fn === true .

this se configura desde el contexto adjunto, es decir, el alcance externo que hace que no se asigne al objeto recién creado.

var Message = (text) => {
  this.text = text;
};
// Throws "TypeError: Message is not a constructor"
var helloMessage = new Message('Hello World!');

Devolución de llamada con contexto dinámico

La función de flecha vincula el context forma estática en la declaración y no es posible hacerlo dinámico. Adjuntar escuchas de eventos a elementos DOM es una tarea común en la programación del lado del cliente. Un evento activa la función de controlador con esto como el elemento de destino.

var button = document.getElementById('myButton');
button.addEventListener('click', () => {
  console.log(this === window); // => true
  this.innerHTML = 'Clicked button';
});

this es la ventana en una función de flecha que se define en el contexto global. Cuando ocurre un evento de clic, el navegador intenta invocar la función del controlador con el contexto del botón, pero la función de flecha no cambia su contexto predefinido. this.innerHTML es equivalente a window.innerHTML y no tiene sentido.

Tienes que aplicar una expresión de función, que permite cambiar esto dependiendo del elemento de destino:

var button = document.getElementById('myButton');
button.addEventListener('click', function() {
  console.log(this === button); // => true
  this.innerHTML = 'Clicked button';
});

Cuando el usuario hace clic en el botón, esto en la función del controlador es el botón. Por this.innerHTML = 'Clicked button' tanto, this.innerHTML = 'Clicked button' modifica correctamente el texto del botón para reflejar el estado de clic.

Referencias: https://rainsoft.io/when-not-to-use-arrow-functions-in-javascript/



De una manera sencilla,

var a =20; function a(){this.a=10; console.log(a);} 
//20, since the context here is window.

Otra instancia:

var a = 20;
function ex(){
this.a = 10;
function inner(){
console.log(this.a); //can you guess the output of this line.
}
inner();
}
var test = new ex();

Respuesta: La consola imprimiría 20.

La razón es que cada vez que se ejecuta una función, se crea su propia pila, en este ejemplo, la exfunción se ejecuta con el newoperador para que se cree un contexto, y cuando innerse ejecuta, JS creará una nueva pila y ejecutará la innerfunción global contextaunque existe una contexto local.

Entonces, si queremos que la innerfunción tenga un contexto local, exentonces debemos vincular el contexto a la función interna.

Las flechas resuelven este problema, en lugar de tomar el Global contextque toman, local contextsi existe alguno. En el given example,tomará new ex()como this.

Por lo tanto, en todos los casos donde la vinculación es explícita, las flechas resuelven el problema por defecto.


De acuerdo con la proposal , las flechas apuntaban a "abordar y resolver varios puntos comunes de la Function Expression tradicional de la Function Expression ". Pretendían mejorar las cosas vinculándolas léxicamente y ofreciendo una sintaxis concisa.

Sin embargo,

  • Uno no puede enlazar consistentemente this léxico
  • La sintaxis de la función de flecha es delicada y ambigua.

Por lo tanto, las funciones de flecha crean oportunidades de confusión y errores, y deben ser excluidas del vocabulario de un programador de JavaScript, reemplazadas con la function exclusivamente.

En cuanto a this lexico

this es problemático

function Book(settings) {
    this.settings = settings;
    this.pages = this.createPages();
}
Book.prototype.render = function () {
    this.pages.forEach(function (page) {
        page.draw(this.settings);
    }, this);
};

Las funciones de flecha intentan solucionar el problema donde necesitamos acceder a una propiedad de this dentro de una devolución de llamada. Ya hay varias formas de hacerlo: se podría asignar this a una variable, usar bind o usar el tercer argumento disponible en los métodos agregados de Array . Sin embargo, las flechas parecen ser la solución más sencilla, por lo que el método se podría reformular de la siguiente manera:

this.pages.forEach(page => page.draw(this.settings));

Sin embargo, considere si el código utiliza una biblioteca como jQuery, cuyos métodos vinculan this especialmente. Ahora, hay dos valores con los que lidiar:

Book.prototype.render = function () {
    var book = this;
    this.$pages.each(function (index) {
        var $page = $(this);
        book.draw(book.currentPage + index, $page);
    });
};

Debemos usar la function para que each pueda enlazar this dinámicamente. No podemos usar una función de flecha aquí.

Tratar con varios de this valores también puede ser confuso, porque es difícil saber de qué habla this autor:

function Reader() {
    this.book.on('change', function () {
        this.reformat();
    });
}

¿El autor realmente Book.prototype.reformat llamar Book.prototype.reformat ? ¿O se olvidó de enlazar this y tenía la intención de llamar a Reader.prototype.reformat ? Si cambiamos el controlador a una función de flecha, nos preguntaremos de forma similar si el autor deseaba this dinámica, pero eligió una flecha porque encajaba en una línea:

function Reader() {
    this.book.on('change', () => this.reformat());
}

Uno puede plantear: "¿Es excepcional que las flechas a veces sean la función incorrecta de usar? Tal vez si raramente necesitamos this valores dinámicos, todavía estaría bien usar flechas la mayor parte del tiempo".

Pero pregúntese lo siguiente: "¿Valdría la pena 'depurar' el código y descubrir que un 'caso extremo' provocó el resultado de un error?" Preferiría evitar problemas no solo la mayor parte del tiempo, sino 100% del tiempo.

Hay una forma mejor: usar siempre la function (para que siempre pueda vincularse dinámicamente), y hacer referencia siempre a través de una variable. Las variables son léxicas y asumen muchos nombres. Asignar this a una variable dejará en claro tus intenciones:

function Reader() {
    var reader = this;
    reader.book.on('change', function () {
        var book = this;
        book.reformat();
        reader.reformat();
    });
}

Además, siempre asignando this a una variable (incluso cuando hay una sola o ninguna otra función) se garantiza que las intenciones de uno se mantengan claras incluso después de que se cambie el código.

Además, this dinámica no es excepcional. jQuery se utiliza en más de 50 millones de sitios web (a partir de este escrito en febrero de 2016). Aquí hay otras API que enlazan dinámicamente:

  • Mocha (~ 120k descargas ayer) expone métodos para sus pruebas a través de this .
  • Grunt (~ 63k descargas ayer) expone métodos para construir tareas a través de this .
  • Backbone (~ 22k descargas ayer) define métodos para acceder a this .
  • Las API de eventos (como las DOM) se refieren a un EventTarget con this .
  • Las API de prototipos que están parcheadas o extendidas se refieren a las instancias con this .

(Estadísticas a través de http://trends.builtwith.com/javascript/jQuery y https://www.npmjs.com .)

Es probable que necesites dinámicos this enlaces ya.

Un léxico this se espera a veces, pero a veces no; así como una dinámica this veces se espera, pero a veces no. Afortunadamente, hay una mejor manera, que siempre produce y comunica el enlace esperado.

Con respecto a la sintaxis concisa

Las funciones de flecha lograron proporcionar una "forma sintáctica más corta" para las funciones. ¿Pero estas funciones más cortas te harán más exitoso?

Es x => x * x "más fácil de leer" que la function (x) { return x * x; } function (x) { return x * x; } ? Tal vez lo sea, porque es más probable que produzca una sola línea de código corta. Accoring to Dyson's La influencia de la velocidad de lectura y la longitud de la línea en la efectividad de la lectura desde la pantalla ,

Una longitud de línea media (55 caracteres por línea) parece admitir una lectura efectiva a velocidades normales y rápidas. Esto produjo el más alto nivel de comprensión. . .

Se hacen justificaciones similares para el operador condicional (ternario) y para las instrucciones if sola línea.

Sin embargo, ¿está realmente escribiendo las funciones matemáticas simples que se proposal ? Mis dominios no son matemáticos, por lo que mis subrutinas rara vez son tan elegantes. Más bien, normalmente veo que las funciones de flecha rompen el límite de una columna y se ajustan a otra línea debido al editor o la guía de estilo, que anula la "legibilidad" según la definición de Dyson.

Uno podría plantear, "¿Qué hay de usar la versión corta para funciones cortas, cuando sea posible?" Pero ahora una regla estilística contradice una restricción de lenguaje: "Intente utilizar la notación de función más corta posible, teniendo en cuenta que a veces solo la notación más larga se unirá a this como se esperaba". Tal confusión hace que las flechas sean particularmente propensas al mal uso.

Hay numerosos problemas con la sintaxis de la función de flecha:

const a = x =>
    doSomething(x);

const b = x =>
    doSomething(x);
    doSomethingElse(x);

Ambas funciones son sintácticamente válidas. Pero doSomethingElse(x); no está en el cuerpo de b , es solo una declaración de nivel superior, con sangría.

Cuando se expande a la forma de bloque, ya no hay un return implícito, que podría olvidarse de restaurar. Pero es posible que la expresión solo haya tenido la intención de producir un efecto secundario, por lo tanto, ¿quién sabe si será necesario un return explícito en el futuro?

const create = () => User.create();

const create = () => {
    let user;
    User.create().then(result => {
        user = result;
        return sendEmail();
    }).then(() => user);
};

const create = () => {
    let user;
    return User.create().then(result => {
        user = result;
        return sendEmail();
    }).then(() => user);
};

Lo que puede entenderse como un parámetro de reposo puede analizarse como el operador de propagación:

processData(data, ...results => {}) // Spread
processData(data, (...results) => {}) // Rest

La asignación se puede confundir con los argumentos predeterminados:

const a = 1;
let x;
const b = x => {}; // No default
const b = x = a => {}; // "Adding a default" instead creates a double assignment
const b = (x = a) => {}; // Remember to add parens

Los bloques parecen objetos

(id) => id // Returns `id`
(id) => {name: id} // Returns `undefined` (it's a labeled statement)
(id) => ({name: id}) // Returns an object

¿Qué significa esto?

() => {}

¿El autor intentó crear una no-op, o una función que devuelve un objeto vacío? (Teniendo esto en cuenta, ¿deberíamos colocar { después => ? ¿Deberíamos limitarnos a la sintaxis de expresión solamente? Eso reduciría aún más la frecuencia de las flechas).

=> ve como <= y >= :

x => 1 ? 2 : 3
x <= 1 ? 2 : 3

if (x => 1) {}
if (x >= 1) {}

Para invocar una expresión de función de flecha inmediatamente, se debe colocar () en el exterior, pero colocar () en el interior es válido y podría ser intencional.

(() => doSomething()()) // Creates function calling value of `doSomething()`
(() => doSomething())() // Calls the arrow function

Aunque, si uno escribe (() => doSomething()()); con la intención de escribir una expresión de función invocada de inmediato, simplemente nada sucederá.

Es difícil argumentar que las funciones de flecha son "más comprensibles" teniendo en cuenta todos los casos anteriores. Uno podría aprender todas las reglas especiales requeridas para utilizar esta sintaxis. ¿Realmente vale la pena?

La sintaxis de la function es excepcionalmente generalizada. Utilizar la function exclusivamente significa que el propio lenguaje evita que uno escriba código confuso. Para escribir procedimientos que deben entenderse sintácticamente en todos los casos, elijo la function .

Respecto a una pauta

Solicita una guía que debe ser "clara" y "coherente". El uso de las funciones de flecha eventualmente resultará en un código lógicamente inválido, sintácticamente válido, con ambas formas de funciones entrelazadas, significativa y arbitrariamente. Por lo tanto, ofrezco lo siguiente:

Guía para la notación de funciones en ES6:

  • Siempre crea procedimientos con function .
  • Siempre asigna this a una variable. No utilice () => {} .

Hace un tiempo, nuestro equipo migró todo su código (una aplicación AngularJS de tamaño mediano) a JavaScript compilado utilizando Traceur Babel . Ahora estoy usando la siguiente regla de oro para las funciones en ES6 y más allá:

  • Utilice la function en el ámbito global y para Object.prototype propiedades de Object.prototype .
  • Utilice la class para los constructores de objetos.
  • Use => cualquier otro lugar.

¿Por qué usar las funciones de flecha en casi todas partes?

  1. Seguridad del alcance: cuando las funciones de flecha se usan de manera consistente, se garantiza que todo use el mismo thisObject como la raíz. Si incluso una única función estándar de devolución de llamada se mezcla con un montón de funciones de flecha, existe la posibilidad de que el alcance se desordene.
  2. Compacidad: las funciones de flecha son más fáciles de leer y escribir. (Esto puede parecer opinado, así que daré algunos ejemplos más adelante).
  3. Claridad: cuando casi todo es una función de flecha, cualquier function regular sobresale inmediatamente para definir el alcance. Un desarrollador siempre puede buscar la siguiente declaración de function para ver qué es este objeto.

¿Por qué siempre usar funciones regulares en el alcance global o en el alcance del módulo?

  1. Para indicar una función que no debe acceder a este thisObject .
  2. El objeto de la window (alcance global) se aborda mejor de manera explícita.
  3. Muchas definiciones de Object.prototype se encuentran en el ámbito global (piense en String.prototype.truncate etc.) y, por lo general, tienen que ser de tipo function . El uso constante de la function en el ámbito global ayuda a evitar errores.
  4. Muchas funciones en el ámbito global son constructores de objetos para definiciones de clase de estilo antiguo.
  5. Las funciones pueden ser nombradas 1 . Esto tiene dos ventajas: (1) Es menos incómodo escribir la function foo(){} que const foo = () => {} , en particular fuera de las llamadas a otras funciones. (2) El nombre de la función se muestra en los seguimientos de pila. Si bien sería tedioso nombrar cada devolución de llamada interna, nombrar todas las funciones públicas es probablemente una buena idea.
  6. Las declaraciones de funciones se hoisted (lo que significa que se puede acceder a ellas antes de declararlas), que es un atributo útil en una función de utilidad estática.


Constructores de objetos

Intentar crear una instancia de una función de flecha produce una excepción:

var x = () => {};
new x(); // TypeError: x is not a constructor

Una ventaja clave de las funciones sobre las funciones de flecha es, por lo tanto, que funciona como constructores de objetos:

function Person(name) {
    this.name = name;
}

Sin embargo, la definición de clase de borrador 2 Harmony funcionalmente idéntica es casi tan compacta:

class Person {
    constructor(name) {
        this.name = name;
    }
}

Espero que el uso de la notación anterior eventualmente se desanime. La notación de constructor de objetos todavía puede ser utilizada por algunos para fábricas de objetos anónimos simples donde los objetos se generan mediante programación, pero no por mucho más.

Cuando se necesita un constructor de objetos, se debe considerar convertir la función a una class como se muestra arriba. La sintaxis también funciona con funciones / clases anónimas.


La legibilidad de las funciones de flecha

El mejor argumento probablemente para adherirse a las funciones regulares (maldito sea la seguridad) es que las funciones de flecha son menos legibles que las funciones normales. Si su código no es funcional en primer lugar, entonces las funciones de flecha pueden no parecer necesarias, y cuando las funciones de flecha no se usan de manera consistente, se ven feas.

ECMAScript ha cambiado bastante desde que ECMAScript 5.1 nos dio el Array.forEach funcional, Array.map y todas estas funciones de programación funcional que nos Array.map usar funciones donde los bucles for se hubieran usado antes. JavaScript asíncrono ha despegado bastante. ES6 también enviará un objeto Promise , lo que significa aún más funciones anónimas. No hay vuelta atrás para la programación funcional. En JavaScript funcional, las funciones de flecha son preferibles a las funciones regulares.

Tomemos, por ejemplo, este pedazo de código 3 (particularmente confuso):

function CommentController(articles) {
    this.comments = [];

    articles.getList()
        .then(articles => Promise.all(articles.map(article => article.comments.getList())))
        .then(commentLists => commentLists.reduce((a, b) => a.concat(b)));
        .then(comments => {
            this.comments = comments;
        })
}

La misma pieza de código con funciones regulares:

function CommentController(articles) {
    this.comments = [];

    articles.getList()
        .then(function (articles) {
            return Promise.all(articles.map(function (article) { 
                return article.comments.getList();
            }));
        })
        .then(function (commentLists) {
            return commentLists.reduce(function (a, b) {
                return a.concat(b); 
            });
        })
        .then(function (comments) {
            this.comments = comments;
        }.bind(this));
}

Si bien cualquiera de las funciones de flecha puede ser reemplazada por una función estándar, habría muy poco que ganar al hacerlo. ¿Qué versión es más legible? Yo diría que el primero.

Creo que la cuestión de si usar las funciones de flecha o las funciones regulares se volverá menos relevante con el tiempo. La mayoría de las funciones se convertirán en métodos de clase, que eliminan la palabra clave de la function , o se convertirán en clases. Las funciones permanecerán en uso para parchear las clases a través del Object.prototype . Mientras tanto, sugiero reservar la palabra clave de function para cualquier cosa que realmente debería ser un método de clase o una clase.

Notas

  1. Las funciones de flecha con nombre se han diferido en la especificación ES6 . Todavía podrían añadirse una versión futura.
  2. De acuerdo con el borrador de la especificación "Las declaraciones / expresiones de clase crean una función de constructor / par de prototipo exactamente como para las declaraciones de función" siempre que una clase no use la palabra clave extend . Una pequeña diferencia es que las declaraciones de clase son constantes, mientras que las declaraciones de funciones no lo son.
  3. Nota sobre los bloques en las funciones de flecha de una sola declaración: Me gusta usar un bloque dondequiera que se llame a una función de flecha solo por efecto secundario (por ejemplo, asignación). De esa forma queda claro que el valor de retorno puede ser descartado.

Además de las excelentes respuestas hasta ahora, me gustaría presentar una razón muy diferente por la que las funciones de flecha son en cierto sentido fundamentalmente mejores que las funciones de JavaScript "normales". En aras de la discusión, asumamos temporalmente que usamos un comprobador de tipos como TypeScript o "Flow" de Facebook. Considere el siguiente módulo de juguete, que es un código ECMAScript 6 válido más anotaciones de tipo Flow: (Incluiré el código sin tipo, que sería el resultado realista de Babel, al final de esta respuesta, para que pueda ejecutarse).

export class C {
  n : number;
  f1: number => number; 
  f2: number => number;

  constructor(){
    this.n = 42;
    this.f1 = (x:number) => x + this.n;
    this.f2 = function (x:number) { return  x + this.n;};
  }
}

Ahora vea qué sucede cuando usamos la clase C de un módulo diferente, como este:

let o = { f1: new C().f1, f2: new C().f2, n: "foo" };
let n1: number = o.f1(1); // n1 = 43
console.log(n1 === 43); // true
let n2: number = o.f2(1); // n2 = "1foo"
console.log(n2 === "1foo"); // true, not a string!

Como puede ver, el verificador de tipos falló aquí: se suponía que f2 devolvía un número, ¡pero devolvía una cadena!

Peor aún, parece que ningún comprobador de tipo concebible puede manejar funciones de JavaScript normales (sin flecha), porque "esto" de f2 no ​​se produce en la lista de argumentos de f2, por lo que no se podría agregar el tipo requerido para "esto" como una anotación a f2.

¿Este problema también afecta a las personas que no usan las verificadoras de tipos? Creo que sí, porque incluso cuando no tenemos tipos estáticos, pensamos como si estuvieran allí. ("Los primeros parámetros deben ser un número, el segundo una cadena", etc.) Un "Este" documento oculto que puede o no ser usado en el cuerpo de la función hace que nuestra contabilidad mental sea más difícil.

Aquí está la versión ejecutable sin tipo, que sería producida por Babel:

class C {
    constructor() {
        this.n = 42;
        this.f1 = x => x + this.n;
        this.f2 = function (x) { return x + this.n; };
    }
}

let o = { f1: new C().f1, f2: new C().f2, n: "foo" };
let n1 = o.f1(1); // n1 = 43
console.log(n1 === 43); // true
let n2 = o.f2(1); // n2 = "1foo"
console.log(n2 === "1foo"); // true, not a string!





arrow-functions