¿Cómo escribo una función de flecha con nombre en ES2015?


204

Tengo una función que estoy tratando de convertir a la nueva sintaxis de flecha en ES6 . Es una función con nombre:

function sayHello(name) {
    console.log(name + ' says hello');
}

¿Hay alguna manera de darle un nombre sin una declaración var:

var sayHello = (name) => {
    console.log(name + ' says hello');
}

Obviamente, solo puedo usar esta función después de haberla definido. Algo como lo siguiente:

sayHello = (name) => {
        console.log(name + ' says hello');
    }

¿Hay una nueva forma de hacer esto en ES6 ?


3
¿No es la sintaxis de punto de flecha para no dar un nombre a la función?
AstroCB

64
No necesariamente, las funciones de flecha mantienen el alcance léxico, por lo que tener una abreviatura para producir funciones con nombre (útil para un seguimiento de pila) con alcance léxico sería bastante útil
Matt Styles

66
¿Qué hay de malo con el segundo fragmento?
Bergi

Sin duda, puede hacer referencia a un nombre asignado dentro del cuerpo de la función: var sayHello = (name) => {console.log (sayHello); }; Y en el caso de las funciones recursivas, a menudo harás exactamente eso. No es un ejemplo súper útil, pero aquí hay una función que se devuelve si no obtiene su argumento: var sayHello = (name) => name? Console.log (name + 'dice hello'): sayHello; sayHello () ('franco'); // -> "Frank dice hola"
Dtipson

44
El título de la pregunta es enormemente engañoso en comparación con su contenido, porque ha descartado la forma en que nombra las funciones de flecha (que es el segundo fragmento).
TJ Crowder

Respuestas:


211

¿Cómo escribo una función de flecha con nombre en ES2015?

Lo hace de la manera que descartó en su pregunta: lo coloca en el lado derecho de una asignación o inicializador de propiedad donde el motor de JavaScript puede usar razonablemente la variable o el nombre de la propiedad como nombre. No hay otra forma de hacerlo, pero hacerlo es correcto y está completamente cubierto por la especificación.

Por especificación, esta función tiene un nombre verdadero sayHello:

var sayHello = name => {
    console.log(name + ' says hello');
};

Esto se define en Operadores de asignación> Semántica de tiempo de ejecución: evaluación donde llama a la SetFunctionNameoperación abstracta (esa llamada se encuentra actualmente en el paso 1.e.iii).

De manera similar, Runtime Semantics: PropertyDefinitionEvaluation llama SetFunctionNamey, por lo tanto, le da a esta función un nombre verdadero:

let o = {
    sayHello: name => {
        console.log(`${name} says hello`);
    }
};

Los motores modernos ya establecen el nombre interno de la función para declaraciones como esa; Edge todavía tiene el bit que lo hace disponible como nameen la instancia de la función detrás de un indicador de tiempo de ejecución.

Por ejemplo, en Chrome o Firefox, abra la consola web y luego ejecute este fragmento:

"use strict";
let foo = () => { throw new Error(); };
console.log("foo.name is: " + foo.name);
try {
  foo();
} catch (e) {
  console.log(e.stack);
}

En Chrome 51 y superior y Firefox 53 y superior (y Edge 13 y superior con una marca experimental), cuando ejecute eso, verá:

foo.name es: foo
Error
    en foo (http://stacksnippets.net/js:14:23)
    en http://stacksnippets.net/js:17:3

Tenga en cuenta la foo.name is: fooy Error...at foo.

En Chrome 50 y versiones anteriores, Firefox 52 y versiones anteriores, y Edge sin la marca experimental, verás esto porque no tienen la Function#namepropiedad (todavía):

foo.name es: 
Error
    en foo (http://stacksnippets.net/js:14:23)
    en http://stacksnippets.net/js:17:3

Tenga en cuenta que el nombre no se encuentra en foo.name is:, pero se muestra en el seguimiento de la pila. Es solo que realmente implementando la name propiedad en la función era de menor prioridad que otras características de ES2015; Chrome y Firefox lo tienen ahora; Edge lo tiene detrás de una bandera, presumiblemente ya no estará detrás de la bandera por mucho tiempo.

Obviamente, solo puedo usar esta función después de haberla definido

Correcto. No hay sintaxis de declaración de función para las funciones de flecha, solo la sintaxis de expresión de función , y no hay una flecha equivalente al nombre en una expresión de función con nombre de estilo antiguo ( var f = function foo() { };). Entonces no hay equivalente a:

console.log(function fact(n) {
    if (n < 0) {
        throw new Error("Not defined for negative numbers");
    }
    return n == 0 ? 1 : n * fact(n - 1);
}(5)); // 120

Tienes que dividirlo en dos expresiones (argumentaría que deberías hacerlo de todos modos ) :

let fact = n => {
    if (n < 0) {
      throw new Error("Not defined for negative numbers.");
    }
    return n == 0 ? 1 : n * fact(n - 1);
};
console.log(fact(5));

Por supuesto, si tiene que poner esto donde se requiere una sola expresión, siempre puede ... usar una función de flecha:

console.log((() => {
    let fact = n => {
        if (n < 0) {
            throw new Error("Not defined for negative numbers.");
        }
        return n == 0 ? 1 : n * fact(n - 1);
    };
    return fact(5);
})()); // 120

No digo que sea bonito, pero funciona si necesitas absolutamente un contenedor de expresión.


¿Cómo evito que se establezca el nombre (como en motores más antiguos)?
trusktr

1
@trusktr: No puedo ver por qué querrías hacerlo, pero si quieres evitarlo: no hagas las cosas anteriores. Por ejemplo, mientras let f = () => { /* do something */ };asigna un nombre a la función, let g = (() => () => { /* do something */ })();no lo hace.
TJ Crowder

Eso es lo que terminé haciendo. Prefiero que las clases anónimas generadas por mi biblioteca de herencia de clases sean anónimas que tener nombres de variables internas que el usuario de mi biblioteca de herencia de clase no necesita pensar, y me gustaría que el usuario final vea un nombre solo si proporcionan un nombre para la clase. ( github.com/trusktr/lowclass )
trusktr

44
@trusktr: La única excepción que hicieron puede adaptarse a sus propósitos, luego: si asigna una función a una propiedad en un objeto existente , el nombre no se establece: o.foo = () => {};no le da un nombre a la función. Esto fue intencional para evitar la fuga de información.
TJ Crowder

Parece que con react-native en algunos dispositivos todavía no devuelve el nombre, o react y react-native / react-native-debugger no recogen el nombre
Zorgatone hace

86

No. La sintaxis de la flecha es una forma abreviada de funciones anónimas. Las funciones anónimas son, bueno, anónimas.

Las funciones con nombre se definen con la functionpalabra clave.


16
Como dijo @ DenysSéguret, no es una forma abreviada para funciones anónimas . Obtendrá una función de enlace duro . Puede ver el resultado recopilado aquí bit.do/es2015-arrow para comprender mejor lo que esto implica ...
sminutoli

16
La primera palabra de esta respuesta es correcta para la pregunta formulada porque el OP descartó la forma en que nombra una función de flecha. Pero el resto de la respuesta es simplemente incorrecta. Busque en la especificación dónde se usa la operación abstractaSetFunctionName . let a = () => {};define una función con nombre (el nombre es a) según la especificación y en los motores modernos (arroje un error para ver); simplemente no admiten la namepropiedad todavía (pero está en las especificaciones y llegarán allí eventualmente).
TJ Crowder

44
@ DenysSéguret: Simplemente puede nombrarlos, está en la especificación. Lo haces de la forma en que el OP descartó en la pregunta, lo que le da a la función (no solo a la variable) un nombre verdadero.
TJ Crowder

55
@TJCrowder Gracias por esa información, y por su útil respuesta . No conocía esta característica (muy muy rara).
Denys Séguret

44
Esta respuesta no es correcta. La pregunta fue respondida correctamente por @TJCrowder a continuación
Drenai

44

Si por "nombre", quiere decir que desea .nameestablecer la propiedad de su función de flecha, tiene suerte.

Si se define una función de flecha en el lado derecho de una expresión de asignación, el motor tomará el nombre en el lado izquierdo y lo usará para establecer la función de flecha .name, por ejemplo

var sayHello = (name) => {
    console.log(name + ' says hello');
}

sayHello.name //=== 'sayHello'

Habiendo dicho eso, su pregunta parece ser más "¿puedo obtener una función de flecha para izar?". Me temo que la respuesta a esa pregunta es un gran "no".


1
En la versión actual de Chrome, al menos, sayHello.name sigue siendo ""; Tenga en cuenta que, como todas las funciones, sayHello es un objeto, por lo que puede definir propiedades arbitrarias si lo desea. No puede escribir en "nombre" ya que es de solo lectura, pero podría decir Hello.my_name = "sayHello"; Sin embargo, no estoy seguro de lo que eso te lleva, ya que no puedes hacer / hacer referencia a eso dentro del cuerpo de la función.
Dtipson

2
Me equivoqué: aunque el nombre no es directamente mutable, puede configurarlo así: Object.defineProperty (sayHello, 'name', {value: 'sayHello'})
Dtipson

1
"Si se define una función de flecha en el lado derecho [..]", ¿cuál es la fuente de esto? No puedo encontrarlo en la especificación. Solo necesito una referencia para cotizar.
Gajus

@Dtipson: Eso es solo porque los motores modernos aún no han implementado la configuración de la namepropiedad en todos los lugares donde la especificación ES2015 lo requiere; ellos lo alcanzarán. Es una de las últimas cosas en la lista de cumplimiento de Chrome e incluso Chrome 50 no lo tiene (Chrome 51 finalmente lo tendrá). Detalles . Pero el OP descartó específicamente hacer esto (extrañamente).
TJ Crowder

¿Puedo poner este nombre en toString? Intenté anularlo, pero luego no sé cómo acceder al cuerpo de la función.
Qwerty

0

Parece que esto será posible con ES7: https://babeljs.io/blog/2015/06/07/react-on-es6-plus#arrow-functions

El ejemplo dado es:

class PostInfo extends React.Component {
  handleOptionsButtonClick = (e) => {
    this.setState({showOptionsModal: true});
  }
}

El cuerpo de las funciones de flecha de ES6 comparte el mismo léxico que el código que las rodea, lo que nos da el resultado deseado debido a la forma en que se definen los inicializadores de propiedades de ES7.

Tenga en cuenta que para que esto funcione con babel, necesitaba habilitar la sintaxis más experimental de la etapa 0 de ES7. En mi archivo webpack.config.js actualicé el cargador de babel así:

{test: /\.js$/, exclude: /node_modules/, loader: 'babel?stage=0'},

66
Esa no es exactamente una función de flecha con nombre. Este es un acceso directo para crear métodos de instancia en el constructor, y me pregunto cómo es relevante aquí.
Bergi

Hola @ Bergi, no estoy seguro de si esta fue la intención del autor original, pero estaba tratando de crear una función con el mismo alcance que el objeto. Si no usamos esta técnica, y queremos tener el alcance apropiado, tenemos que introducirla en el constructor de la clase. this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);
Hamish Currie

8
Uh, puedes usar fácilmente lo constructor() { this.handleOptionsButtonClick = (e) => {…}; }que es totalmente equivalente al código en tu respuesta, pero ya funciona en ES6. No es necesario usar .bind.
Bergi

1
Por cierto, creo que estás confundiendo "función con nombre" con "método (instancia)" y "alcance" con " thiscontexto"
Bergi

1
@tdecs: Sí, puedes; Jörg está equivocado. let a = () => {};define una función de flecha con nombre llamada a. Detalles .
TJ Crowder

0

En realidad, parece que una forma de nombrar las funciones de flecha (al menos a partir de Chrome 77 ...) es hacer esto:

"use strict";
let fn_names = {};
fn_names.foo = () => { throw new Error(); };
console.log("foo.name is: " + foo.name);
try {
  foo();
} catch (e) {
  console.log(e.stack);
}

ingrese la descripción de la imagen aquí


podría, teóricamente, crear una macro babel fn('yourName', ()=>{}), lo que podría mantener el 100% de flecha correcta semántica de función, o mejor aún, un plugin para babel "flecha sintaxis de la función denominada": fn yourName() => {}. Cualquiera de estos se compilaría hasta el código anterior. Creo que las flechas con nombre serían tan BADA55
Devin G Rhode

-1

para escribir una función de flecha con nombre, puedes usar el siguiente ejemplo, donde tengo una clase llamada LoginClass y dentro de esta clase escribí una flecha llamada función , llamada successAuth clase LoginClass {

    constructor() {

    }

    successAuth = (dataArgs)=> { //named arow function

    }

}

1
Siempre es mejor agregar una explicación al código que está publicando como respuesta, de modo que ayude a los visitantes a comprender por qué esta es una buena respuesta.
abarisone

Esto hace lo que el OP dijo que no quiere hacer, y se basa en esta propuesta que está solo en la Etapa 2 y probablemente ni siquiera va a hacer ES2017 (pero creo que es probable que haga ES2018, y los transpiladores lo han apoyado por meses). También duplica efectivamente varias respuestas anteriores, lo que no es útil.
TJ Crowder

-2

ESTO ES ES6

Sí, creo que lo que buscas es algo como esto:

const foo = (depth) => {console.log("hi i'm Adele")}
foo -> // the function itself
foo() -> // "hi i'm Adele"

Intenté este ejemplo, funciona bien. ¿Alguna buena razón para dar votos negativos? ¿Este uso crea efectos secundarios no deseados o me falta algún punto importante? Su ayuda para aclarar mis dudas será apreciada.
FullMoon

@GaneshAdapa: Espero que las votaciones negativas se deban a que esto sugiere sugerir exactamente lo que el OP dijo que no quería hacer, sin dar más información.
TJ Crowder

-2

Puede omitir la parte de la función y la parte de la flecha para crear funciones. Ejemplo:

 class YourClassNameHere{

   constructor(age) {
     this.age = age;
   }

   foo() {
     return "This is a function with name Foo";
   }

   bar() {
     return "This is a function with name bar";
   }

 }

let myVar = new YourClassNameHere(50);
myVar.foo();
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.