Le "funzioni freccia" e le "funzioni" sono equivalenti / intercambiabili?
Le funzioni freccia in ES2015 forniscono una sintassi più concisa.
- Posso sostituire tutte le mie dichiarazioni / espressioni di funzione con funzioni freccia ora?
- A cosa devo prestare attenzione?
Esempi:
Funzione costruttore
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: () => {
// ...
}
};
Richiami
setTimeout(function() {
// ...
}, 500);
// vs
setTimeout(() => {
// ...
}, 500);
Funzioni variadiche
function sum() {
let args = [].slice.call(arguments);
// ...
}
// vs
const sum = (...args) => {
// ...
};
Risposte
tl; dr: No! Le funzioni freccia e le dichiarazioni / espressioni di funzione non sono equivalenti e non possono essere sostituite ciecamente.
Se la funzione che si desidera sostituire non non usare this
, arguments
e non viene chiamato con new
, allora sì.
Come tante volte: dipende . Le funzioni freccia hanno un comportamento diverso rispetto alle dichiarazioni / espressioni di funzione, quindi diamo prima un'occhiata alle differenze:
1. Lessico this
earguments
Le funzioni freccia non hanno le proprie this
o arguments
associazioni. Invece, questi identificatori vengono risolti nell'ambito lessicale come qualsiasi altra variabile. Ciò significa che all'interno di una funzione freccia this
e si arguments
riferiscono ai valori di this
e arguments
nell'ambiente la funzione freccia è definita in (cioè "fuori" dalla 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 di funzione, si this
riferisce all'oggetto creato all'interno di createObject
. Nel caso funzione freccia, this
si riferisce al this
di createObject
sé.
Ciò rende le funzioni delle frecce utili se è necessario accedere this
all'ambiente corrente:
// currently common pattern
var that = this;
getData(function(data) {
that.data = data;
});
// better alternative with arrow functions
getData(data => {
this.data = data;
});
Notare che questo significa anche che non è possibile impostare una funzione freccia this
con .bind
o .call
.
Se non hai molta familiarità this
, considera la lettura
2. Le funzioni freccia non possono essere chiamate con new
ES2015 distingue tra funzioni che possono essere richiamate e funzioni che possono essere costruite . Se una funzione è costruibile, può essere chiamata con new
, ie new User()
. Se una funzione è richiamabile, può essere chiamata senza new
(cioè chiamata di funzione normale).
Le funzioni create tramite dichiarazioni / espressioni di funzione sono sia costruibili che richiamabili.
Le funzioni (e i metodi) delle frecce sono solo richiamabili.
class
i costruttori sono solo costruibili.
Se stai cercando di chiamare una funzione non richiamabile o di costruire una funzione non costruibile, riceverai un errore di runtime.
Sapendo questo, possiamo affermare quanto segue.
Sostituibile:
- Funzioni che non usano
this
oarguments
. - Funzioni utilizzate con
.bind(this)
Non sostituibile:
- Funzioni del costruttore
- Funzione / metodi aggiunti a un prototipo (perché di solito usano
this
) - Funzioni variadiche (se usano
arguments
(vedi sotto))
Diamo uno sguardo più da vicino a questo usando i tuoi esempi:
Funzione costruttore
Questo non funzionerà perché le funzioni freccia non possono essere chiamate con new
. Continua a usare una dichiarazione / espressione di funzione o usa class
.
Metodi prototipo
Molto probabilmente no, perché i metodi del prototipo di solito usano this
per accedere all'istanza. Se non lo usano this
, puoi sostituirlo. Tuttavia, se ti interessa principalmente la sintassi concisa, usa class
la 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() {
// ...
},
};
Richiami
Dipende. Dovresti assolutamente sostituirlo se stai this
usando un alias esterno o stai usando .bind(this)
:
// old
setTimeout(function() {
// ...
}.bind(this), 500);
// new
setTimeout(() => {
// ...
}, 500);
Ma: se il codice che chiama il callback si imposta esplicitamente this
su un valore specifico, come spesso accade con i gestori di eventi, specialmente con jQuery, e il callback usa this
(o arguments
), non puoi usare una funzione freccia!
Funzioni variadiche
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:
- Quando dovrei usare le funzioni Arrow in ECMAScript 6?
- Le funzioni freccia di ES6 hanno i propri argomenti o no?
- Quali sono le differenze (se presenti) tra le funzioni freccia ES6 e le funzioni associate a Function.prototype.bind?
- Come utilizzare le funzioni freccia (campi di classi pubbliche) come metodi di classe?
Ulteriori risorse:
Funzioni freccia => la migliore funzionalità ES6 finora. Sono un'aggiunta tremendamente potente a ES6, che uso costantemente.
Aspetta, non puoi usare la funzione freccia ovunque nel tuo codice, non funzionerà in tutti i casi come this
dove 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 eventi.
Le funzioni freccia NON dovrebbero essere utilizzate perché:
Non hanno
this
Utilizza lo "scoping lessicale" per capire quale
this
dovrebbe essere il valore di " ". In parole semplici, scoping lessicale utilizza "this
" dall'interno del corpo della funzione.Non hanno
arguments
Le funzioni freccia non hanno un
arguments
oggetto. Ma la stessa funzionalità può essere ottenuta utilizzando i parametri di riposo.let sum = (...args) => args.reduce((x, y) => x + y, 0)
sum(3, 3, 1) // output - 7
"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:
- Non utilizzare per aggiungere una funzione come proprietà nell'oggetto letterale perché non possiamo accedervi.
- Le espressioni di funzione sono le migliori per i metodi oggetto. Arrow funzioni sono i migliori per le richiamate o metodi come
map
,reduce
oforEach
. - Usa le dichiarazioni di funzione per le funzioni che chiameresti per nome (perché sono sollevate).
- Usa le funzioni freccia per i callback (perché tendono ad essere più concisi).
Per utilizzare le funzioni freccia con function.prototype.call
, ho creato una funzione di supporto sul prototipo dell'oggetto:
// Using
// @func = function() {use this here} or This => {use This here}
using(func) {
return func.call(this, this);
}
utilizzo
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