javascript - usar - sintaxis funcion flecha




¿Cuándo debo usar las funciones de flecha en ECMAScript 6? (5)

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.


Funciones de flecha: la característica más utilizada de ES6 hasta ahora ...

Uso: Todas las funciones de ES5 deben reemplazarse con las funciones de flecha de ES6, excepto en los siguientes escenarios:

Las funciones de flecha NO deben ser usadas:

  1. Cuando queremos función de elevación.
    • Como las funciones de flecha son anónimas.
  2. Cuando queremos usar this / arguments en una función.
    • Como las funciones de flecha no tienen this / arguments propios, dependen de su contexto externo.
  3. Cuando queremos usar la función nombrada.
    • Como las funciones de flecha son anónimas.
  4. Cuando queremos usar la función como constructor
    • Como las funciones de flecha no tienen su propio this .
  5. Cuando queremos agregar la función como una propiedad en el literal del objeto y usar el objeto en ella
    • Como no podemos acceder a this (que debería ser objeto en sí).

Entendamos algunas de las variantes de las funciones de flecha para entender mejor:

Variante 1 : cuando queremos pasar más de un argumento a una función y devolverle algún valor.

Versión ES5 :

var multiply = function (a,b) {
    return a*b;
};
console.log(multiply(5,6)); //30

Versión ES6 :

var multiplyArrow = (a,b) => a*b;
console.log(multiplyArrow(5,6)); //30

Nota: la palabra clave de function NO es obligatoria. => se requiere. {} son opcionales, cuando no proporcionamos la return {} se agrega implícitamente por JavaScript y cuando proporcionamos {} necesitamos agregar la return si la necesitamos.

Variante 2 : cuando queremos pasar SÓLO un argumento a una función y devolverle algún valor.

Versión ES5 :

var double = function(a) {
    return a*2;
};
console.log(double(2)); //4

Versión ES6 :

var doubleArrow  = a => a*2;
console.log(doubleArrow(2)); //4

Nota: Al pasar solo un argumento podemos omitir paréntesis () .

Variante 3 : cuando NO queremos pasar ningún argumento a una función y NO queremos devolver ningún valor.

Versión ES5 :

var sayHello = function() {
    console.log("Hello");
};
sayHello(); //Hello

Versión ES6 :

var sayHelloArrow = () => {console.log("sayHelloArrow");}
sayHelloArrow(); //sayHelloArrow

Variante 4 : Cuando queremos regresar explícitamente de las funciones de flecha.

Versión ES6 :

var increment = x => {
  return x + 1;
};
console.log(increment(1)); //2

Variante 5 : Cuando queremos devolver un objeto desde las funciones de flecha.

Versión ES6 :

var returnObject = () => ({a:5});
console.log(returnObject());

Nota: Necesitamos envolver el objeto entre paréntesis () contrario, JavaScript no puede diferenciar entre un bloque y un objeto.

Variante 6 : Las funciones de flecha NO tienen arguments (una matriz como objeto) propios, dependen del contexto externo para los arguments .

Versión ES6 :

function foo() {
  var abc = i => arguments[0];
  console.log(abc(1));
};    
foo(2); // 2

Nota: foo es una función ES5, con una matriz de arguments como un objeto y un argumento que se le pasa es 2 por lo que los arguments[0] para foo son 2.

abc es una función de flecha ES6 ya que NO tiene sus propios arguments por lo tanto, imprime los arguments[0] de foo en su contexto externo.

Variante 7 : Las funciones de flecha NO tienen this propio, dependen del contexto externo para this

Versión ES5 :

var obj5 = {
  greet: "Hi, Welcome ",
  greetUser : function(user) {
        setTimeout(function(){
        console.log(this.greet + ": " +  user); // "this" here is undefined.
        });
     }
};

obj5.greetUser("Katty"); //undefined: Katty

Nota: la devolución de llamada pasada a setTimeout es una función de ES5 y tiene su propia función, que no está definida en use-strict entorno de use-strict , por lo que obtenemos resultados:

undefined: Katty

Versión ES6 :

var obj6 = {
  greet: "Hi, Welcome ",
  greetUser : function(user) {
    setTimeout(() => console.log(this.greet + ": " +  user)); 
      // this here refers to outer context
   }
};

obj6.greetUser("Katty"); //Hi, Welcome: Katty

Nota: La devolución de llamada pasada a setTimeout es una función de flecha de ES6 y NO tiene su propia función, por lo que la toma de su contexto externo, que es greetUser que tiene this que es obj6 por lo que obtenemos resultados:

Hi, Welcome: Katty

Misceláneo: No podemos usar new funciones de flecha.Las funciones de flecha no tienen prototypepropiedad. NO tenemos vinculación de thiscuando la función de flecha se invoca a través de applyo call.


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 () => {} .

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.






arrow-functions