Según la propuesta , las flechas apuntaban "a abordar y resolver varios puntos de dolor comunes de los tradicionales Function Expression
". Tenían la intención de mejorar las cosas mediante la vinculación this
léxica y ofreciendo una sintaxis concisa.
Sin embargo,
- Uno no puede unirse 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 para la confusión y los errores, y deben excluirse del vocabulario de un programador de JavaScript, reemplazarse function
exclusivamente.
En cuanto a léxico this
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 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 Array
métodos agregados. Sin embargo, las flechas parecen ser la solución más simple, por lo que el método podría refactorizarse así:
this.pages.forEach(page => page.draw(this.settings));
Sin embargo, considere si el código usó una biblioteca como jQuery, cuyos métodos se unen this
especialmente. Ahora, hay dos this
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 function
para each
unir this
dinámicamente. No podemos usar una función de flecha aquí.
Tratar con múltiples this
valores también puede ser confuso, porque es difícil saber de qué this
estaba hablando un autor:
function Reader() {
this.book.on('change', function () {
this.reformat();
});
}
¿El autor realmente tenía la intención de llamar Book.prototype.reformat
? ¿O se olvidó de atar this
y tenía la intención de llamar Reader.prototype.reformat
? Si cambiamos el controlador a una función de flecha, nos preguntaremos de manera similar si el autor quería la dinámica this
, pero elegimos una flecha porque cabe en una línea:
function Reader() {
this.book.on('change', () => this.reformat());
}
Uno puede plantearse: "¿Es excepcional que las flechas a veces sean una función incorrecta? Tal vez si raramente necesitamos this
valores dinámicos , entonces estaría bien usar flechas la mayor parte del tiempo".
Pero pregúntese esto: "¿Valdría la pena 'depurar el código y descubrir que el resultado de un error fue provocado por un' caso límite '?" Preferiría evitar problemas no solo la mayor parte del tiempo, sino 100% del tiempo
Hay una mejor manera: usar siempre function
(por this
lo que siempre se puede vincular dinámicamente) y siempre hacer referencia a this
través de una variable. Las variables son léxicas y asumen muchos nombres. Asignar this
a una variable aclarará sus intenciones:
function Reader() {
var reader = this;
reader.book.on('change', function () {
var book = this;
book.reformat();
reader.reformat();
});
}
Además, siempre asignar this
a una variable (incluso cuando hay una sola this
o ninguna otra función) asegura que las intenciones de uno permanezcan claras incluso después de cambiar el código.
Además, la dinámica this
no es excepcional. jQuery se usa en más de 50 millones de sitios web (a partir de este escrito en febrero de 2016). Aquí hay otras API que se vinculan this
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 crear tareas a través de
this
.
- Backbone (~ 22k descargas ayer) define los métodos de acceso
this
.
- Las API de eventos (como los DOM) se refieren a un
EventTarget
con this
.
- Las API de Prototypal que están parcheadas o extendidas se refieren a instancias con
this
.
(Estadísticas a través de http://trends.builtwith.com/javascript/jQuery y https://www.npmjs.com ).
Es probable que ya requiera this
enlaces dinámicos .
A this
veces se espera un léxico , pero a veces no; así como a this
veces se espera una dinámica , pero a veces no. Afortunadamente, hay una mejor manera, que siempre produce y comunica el enlace esperado.
En cuanto 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 function (x) { return x * x; }
? Tal vez lo sea, porque es más probable que produzca una única línea de código corta. De acuerdo con la influencia de Dyson 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 apoyar la 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 if
declaraciones de una sola línea .
Sin embargo, ¿ realmente está escribiendo las funciones matemáticas simples anunciadas en la propuesta ? Mis dominios no son matemáticos, por lo que mis subrutinas rara vez son tan elegantes. Por el contrario, comúnmente 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 plantearse: "¿Qué tal si usamos la versión corta para funciones cortas, cuando sea posible?" Pero ahora una regla estilística contradice una restricción del lenguaje: "Trate de usar 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á this
como se esperaba". Tal combinación hace que las flechas sean particularmente propensas al mal uso.
Existen 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 alto nivel mal sangrada.
Al expandirse a la forma de bloque, ya no hay un implícito return
, que uno podría olvidar restaurar. Pero la expresión solo puede haber tenido la intención de producir un efecto secundario, entonces, ¿quién sabe si return
será necesario un 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 se puede considerar como un parámetro de descanso se puede analizar 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?
() => {}
¿Pretendía el autor crear un no-op, o una función que devuelva un objeto vacío? (Con esto en mente, ¿deberíamos ubicarnos {
después =>
? ¿Deberíamos restringirnos solo a la sintaxis de la expresión? Eso reduciría aún más la frecuencia de las flechas).
=>
se parece <=
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 colocarlo ()
en el interior es válido y podría ser intencional.
(() => doSomething()()) // Creates function calling value of `doSomething()`
(() => doSomething())() // Calls the arrow function
Sin embargo, si uno escribe (() => doSomething()());
con la intención de escribir una expresión de función invocada inmediatamente, simplemente no sucederá nada.
Es difícil argumentar que las funciones de flecha son "más comprensibles" con todos los casos anteriores en mente. Uno podría aprender todas las reglas especiales requeridas para utilizar esta sintaxis. ¿Realmente vale la pena?
La sintaxis de function
es excepcionalmente generalizada. Usar function
exclusivamente significa que el lenguaje mismo evita que uno escriba código confuso. Para escribir procedimientos que deberían entenderse sintácticamente en todos los casos, elijo function
.
Sobre una directriz
Solicita una directriz que debe ser "clara" y "coherente". El uso de las funciones de flecha eventualmente dará como resultado un código sintácticamente válido, lógicamente inválido, con ambas formas de función entrelazadas, de manera significativa y arbitraria. Por lo tanto, ofrezco lo siguiente:
Pauta para la notación de funciones en ES6:
- Siempre cree procedimientos con
function
.
- Asignar siempre
this
a una variable. No utilice () => {}
.
Fixed this bound to scope at initialisation
como una limitación?