Le "Funzioni freccia" e le "Funzioni" sono equivalenti / scambiabili?


521

Le funzioni freccia in ES2015 forniscono una sintassi più concisa.

  • Ora posso sostituire tutte le dichiarazioni / espressioni delle mie funzioni con le funzioni freccia?
  • Cosa devo cercare?

Esempi:

Funzione di costruzione

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

// vs

const User = name => {
  this.name = name;
};

Metodi prototipo

User.prototype.getName = function() {
  return this.name;
};

// vs

User.prototype.getName = () => this.name;

Metodi oggetto (letterali)

const obj = {
  getName: function() {
    // ...
  }
};

// vs

const obj = {
  getName: () => {
    // ...
  }
};

callback

setTimeout(function() {
  // ...
}, 500);

// vs

setTimeout(() => {
  // ...
}, 500);

Funzioni variabili

function sum() {
  let args = [].slice.call(arguments);
  // ...
}

// vs
const sum = (...args) => {
  // ...
};

5
Domande simili sulle funzioni delle frecce sono emerse sempre più con ES2015 che sta diventando sempre più popolare. Non pensavo che ci fosse una buona domanda / risposta canonica per questo problema, quindi ho creato questo. Se pensi che ce ne sia già uno buono, per favore fatemelo sapere e lo chiuderò come duplicato o lo cancellerò. Sentiti libero di migliorare gli esempi o aggiungerne di nuovi.
Felix Kling,

2
Che dire di JavaScript ecma6 modificare la normale funzione in funzione freccia ? Certo, una domanda normale non può mai essere buona e generica come una specificamente scritta per essere canonica.
Bergi,

Guarda questo esempio di Plnkr La variabile ha incrementi thismolto diversi timesCalledsolo di 1 ogni volta che viene chiamato il pulsante. Il che risponde alla mia domanda personale: .click( () => { } )ed .click(function() { }) entrambi creano lo stesso numero di funzioni quando vengono utilizzate in un ciclo come puoi vedere dal conteggio di Guid nel Plnkr.
abbaf33f,

Risposte:


750

tl; dr: No! Le funzioni freccia e le dichiarazioni / espressioni di funzione non sono equivalenti e non possono essere sostituite alla cieca.
Se la funzione che si desidera sostituire non non usare this, argumentse non viene chiamato con new, allora sì.


Come spesso: dipende . Le funzioni freccia hanno un comportamento diverso rispetto alle dichiarazioni / espressioni di funzioni, quindi diamo prima un'occhiata alle differenze:

1. Lessico thisearguments

Le funzioni freccia non hanno le proprie thiso le argumentsassociazioni. Invece, tali identificatori vengono risolti nell'ambito lessicale come qualsiasi altra variabile. Ciò significa che all'interno di una funzione freccia, thise si argumentsriferiscono ai valori di thise argumentsnell'ambiente la funzione freccia è definita in (cioè "al di fuori" della funzione freccia):

// Example using a function expression
function createObject() {
  console.log('Inside `createObject`:', this.foo);
  return {
    foo: 42,
    bar: function() {
      console.log('Inside `bar`:', this.foo);
    },
  };
}

createObject.call({foo: 21}).bar(); // override `this` inside createObject

// Example using a arrow function
function createObject() {
  console.log('Inside `createObject`:', this.foo);
  return {
    foo: 42,
    bar: () => console.log('Inside `bar`:', this.foo),
  };
}

createObject.call({foo: 21}).bar(); // override `this` inside createObject

Nel caso dell'espressione della funzione, si thisriferisce all'oggetto creato all'interno di createObject. Nel caso funzione freccia, thissi riferisce al thisdi createObjectsé.

Ciò rende utili le funzioni freccia se è necessario accedere thisall'ambiente corrente:

// currently common pattern
var that = this;
getData(function(data) {
  that.data = data;
});

// better alternative with arrow functions
getData(data => {
  this.data = data;
});

Si noti che ciò significa anche che non è possibile impostare una funzione freccia thiscon .bindo .call.

Se non hai molta familiarità this, considera la lettura

2. Le funzioni freccia non possono essere richiamate con new

ES2015 distingue tra funzioni abilitabili alla chiamata e funzioni costruibili . Se una funzione è costruibile, può essere chiamata con new, ad es new User(). Se una funzione è richiamabile, può essere richiamata senza new(cioè chiamata di funzione normale).

Le funzioni create tramite dichiarazioni / espressioni di funzioni sono sia costruibili che richiamabili.
Le funzioni (e i metodi) delle frecce sono solo richiamabili. classi costruttori sono costruibili solo.

Se si sta tentando di chiamare una funzione non richiamabile o di costruire una funzione non costruibile, verrà visualizzato un errore di runtime.


Sapendo questo, possiamo affermare quanto segue.

Sostituibile:

  • Funzioni che non usano thiso arguments.
  • Funzioni utilizzate con .bind(this)

Non sostituibile:

  • Funzioni del costruttore
  • Funzione / metodi aggiunti a un prototipo (perché di solito usano this)
  • Funzioni variabili (se usano arguments(vedi sotto))

Diamo un'occhiata più da vicino usando i tuoi esempi:

Funzione di costruzione

Questo non funzionerà perché non è possibile chiamare le funzioni freccia new. Continuare a utilizzare una dichiarazione / espressione di funzione o utilizzare class.

Metodi prototipo

Molto probabilmente no, perché i metodi prototipo di solito usano thisper accedere all'istanza. Se non lo usano this, puoi sostituirlo. Tuttavia, se ti interessa principalmente la sintassi concisa, usa classcon la sua sintassi del metodo conciso:

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

  getName() {
    return this.name;
  }
}

Metodi oggetto

Allo stesso modo per i metodi in un oggetto letterale. Se il metodo desidera fare riferimento all'oggetto stesso tramite this, continuare a utilizzare le espressioni di funzione o utilizzare la nuova sintassi del metodo:

const obj = {
  getName() {
    // ...
  },
};

callback

Dipende. Dovresti assolutamente sostituirlo se stai aliasando l'esterno thiso stai usando .bind(this):

// old
setTimeout(function() {
  // ...
}.bind(this), 500);

// new
setTimeout(() => {
  // ...
}, 500);

Ma: se il codice che chiama il callback si imposta esplicitamente thissu un valore specifico, come spesso accade con i gestori di eventi, in particolare con jQuery, e il callback utilizza this(o arguments), non è possibile utilizzare una funzione freccia!

Funzioni variabili

Poiché le funzioni freccia non hanno le proprie arguments, non è possibile sostituirle semplicemente con una funzione freccia. Tuttavia, ES2015 introduce un'alternativa all'utilizzo arguments: il parametro rest .

// old
function sum() {
  let args = [].slice.call(arguments);
  // ...
}

// new
const sum = (...args) => {
  // ...
};

Domanda correlata:

Ulteriori risorse:


6
Forse vale la pena ricordare che anche il lessico thisinfluisce supere che non hanno .prototype.
loganfsmyth,

1
Sarebbe anche utile menzionare che non sono sintatticamente intercambiabili: una funzione freccia ( AssignmentExpression) non può essere semplicemente rilasciata ovunque un'espressione di funzione ( PrimaryExpression) può e fa inciampare le persone abbastanza frequentemente (specialmente perché ci sono state analisi errori nelle principali implementazioni di JS).
JMM

@JMM: " coinvolge le persone abbastanza frequentemente" puoi fornire un esempio concreto? Sfogliando le specifiche, sembra che i luoghi in cui è possibile inserire una FE ma non una AF comporterebbero comunque errori di runtime ...
Felix Kling

Certo, intendo cose come cercare di invocare immediatamente una funzione freccia come un'espressione di funzione ( () => {}()) o fare qualcosa di simile x || () => {}. Questo è ciò che intendo: errori di runtime (analisi). (E anche se è così, abbastanza frequentemente le persone pensano che l'errore sia errato.) Stai solo cercando di coprire errori logici che passerebbero inosservati perché non necessariamente errori quando analizzati o eseguiti? new'uno è un errore di runtime giusto?
JMM

Ecco alcuni link che si presentano allo stato selvatico: substack / node-browserify # 1499 , babel / babel-eslint # 245 (questa è una freccia asincrona, ma penso che sia lo stesso problema di base), e un sacco di problemi su Babele che è difficile da trovare ora, ma ecco un T2847 .
JMM

11

Funzioni freccia => migliore funzionalità ES6 finora. Sono un'aggiunta straordinariamente potente a ES6, che uso costantemente.

Aspetta, non puoi usare la funzione freccia ovunque nel tuo codice, non funzionerà in tutti i casi come thisdove le funzioni freccia non sono utilizzabili. Senza dubbio, la funzione freccia è una grande aggiunta che porta la semplicità del codice.

Ma non è possibile utilizzare una funzione freccia quando è richiesto un contesto dinamico: definire metodi, creare oggetti con costruttori, ottenere l'obiettivo da questo quando si gestiscono gli eventi.

Le funzioni freccia NON devono essere utilizzate perché:

  1. Non hanno this

    Usa "scoping lessicale" per capire quale thisdovrebbe essere il valore di " ". In parole semplici scoping lessicale usa " this" dall'interno del corpo della funzione.

  2. Non hanno arguments

    Le funzioni freccia non hanno un argumentsoggetto. Ma la stessa funzionalità può essere raggiunta usando i parametri di riposo.

    let sum = (...args) => args.reduce((x, y) => x + y, 0) sum(3, 3, 1) // output - 7 `

  3. Non possono essere utilizzati con new

    Le funzioni freccia non possono essere costruttrici perché non hanno una proprietà prototipo.

Quando utilizzare la funzione freccia e quando no:

  1. Non utilizzare per aggiungere la funzione come proprietà nell'oggetto letterale perché non è possibile accedervi.
  2. Le espressioni di funzione sono le migliori per i metodi oggetto. Arrow funzioni sono i migliori per le richiamate o metodi come map, reduceo forEach.
  3. Usa le dichiarazioni di funzione per le funzioni che chiameresti per nome (perché sono issate).
  4. Utilizzare le funzioni freccia per i callback (perché tendono ad essere terser).

2
il 2. Non hanno argomenti, mi dispiace non è vero, si può avere argomento senza usare l'operatore ..., forse vuoi dire che non hai array come argomento
Carmine Tambascia

@CarmineTambascia Leggi qui l' argumentsoggetto speciale che non è disponibile nelle funzioni freccia: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
vichle

0

Per utilizzare le funzioni freccia con function.prototype.call, ho creato una funzione di supporto sul prototipo oggetto:

  // Using
  // @func = function() {use this here} or This => {use This here}
  using(func) {
    return func.call(this, this);
  }

uso

  var obj = {f:3, a:2}
  .using(This => This.f + This.a) // 5

modificare

Non hai bisogno di un aiuto. Potresti fare:

var obj = {f:3, a:2}
(This => This.f + This.a).call(undefined, obj); // 5
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.