Una funzione a freccia ha una sintassi più compatta rispetto alla notazione a funzione e non associa i propri this
, arguments, super o new.target. Le funzioni a freccia sono sempre anonime. Questa notazione è maggiormente indicata per le funzioni che non sono metodi, e non possono essere usate come costruttori.
Sintassi
Sintassi di base
(param1, param2, …, paramN) => { statements } (param1, param2, …, paramN) => expression // equivalente a: (param1, param2, …, paramN) => { return expression; } // Le Parentesi sono opzionali se è presente un solo parametro: (singleParam) => { statements } singleParam => { statements } // Una funzione senza parametri richiede comunque le parentesi: () => { statements } () => expression // equivalente a: () => { return expression; }
Sintassi avanzata
// Il body tra parentesi indica la restituzione di un oggetto: params => ({foo: bar}) // Sono supportati ...rest e i parametri di default (param1, param2, ...rest) => { statements } (param1 = defaultValue1, param2, …, paramN = defaultValueN) => { statements } // Si può anche destrutturare all'interno della lista dei parametri var f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c; f(); // 6
Esempi dettagliati di sintassi sono disponibili qui.
Descrizione
Vedi anche "ES6 In Depth: Arrow functions" su hacks.mozilla.org.
L'introduzione delle funzioni a freccia è stata influenzata da due fattori: sintassi più compatta e la non associazione di this
.
Funzioni più corte
In alcuni pattern, è meglio avere funzioni più corte. Per comparazione:
var materials = [
"Hydrogen",
"Helium",
"Lithium",
"Beryllium"
];
materials.map(function(material) {
return material.length;
}); // [8, 6, 7, 9]
materials.map((material) => {
return material.length;
}); // [8, 6, 7, 9]
materials.map(material => material.length); // [8, 6, 7, 9]
Mancato binding di this
Prima delle funzioni a freccia, ogni nuova funzione definiva il proprio this
(un nuovo oggetto nel caso di un costruttore, undefined se una funzione viene chiamata in strict mode, l'oggetto di contesto se la funzione viene chiamata come "metodo", etc.). Questo è risultato fastidioso in uno stile di programmazione orientato agli oggetti.
function Person() {
// The Person() constructor defines `this` as an instance of itself.
this.age = 0;
setInterval(function growUp() {
// In non-strict mode, the growUp() function defines `this`
// as the global object, which is different from the `this`
// defined by the Person() constructor.
this.age++;
}, 1000);
}
var p = new Person();
In ECMAScript 3/5, questo problema veniva aggirato assegnando il valore this a una variabile.
function Person() {
var that = this;
that.age = 0;
setInterval(function growUp() {
// The callback refers to the `that` variable of which
// the value is the expected object.
that.age++;
}, 1000);
}
In alternativa, poteva essere usato Function.prototype.bind per assegnare il valore corretto di this da usare nella funzione growUp()
.
Una funziona a freccia invece non crea il proprio this
, e quindi this
mantiene il significato che aveva all'interno dello scope genitore. Perciò il codice seguente funziona come ci si aspetta.
function Person(){
this.age = 0;
setInterval(() => {
this.age++; // |this| properly refers to the person object
}, 1000);
}
var p = new Person();
Relazione con strict mode
Poiché this
è lessicale, le regole di strict mode relative a this
sono semplicemente ignorate.
var f = () => {'use strict'; return this};
f() === window; // o l'oggetto globale
Il resto delle regole si applica normalmente.
Invocazione attraverso call or apply
Poiché this
non viene assegnato nelle funzioni a freccia, i metodi call()
o apply()
possono passare solo come argomenti; this
viene ignorato:
var adder = {
base : 1,
add : function(a) {
var f = v => v + this.base;
return f(a);
},
addThruCall: function(a) {
var f = v => v + this.base;
var b = {
base : 2
};
return f.call(b, a);
}
};
console.log(adder.add(1)); // This would log to 2
console.log(adder.addThruCall(1)); // This would log to 2 still
Mancato binding di arguments
Le funzioni a freccia non definiscono il proprio arguments.
Perciò, arguments
è semplicemente una reference alla variabile nello scope genitore.
var arguments = 42;
var arr = () => arguments;
arr(); // 42
function foo() {
var f = (i) => arguments[0]+i; // foo's implicit arguments binding
return f(2);
}
foo(1); // 3
Le funzioni a freccia non hanno il proprio oggetto arguments
, ma in molti casi i parametri rest rappresentano una valida alternativa:
function foo() {
var f = (...args) => args[0];
return f(2);
}
foo(1); // 2
Funzioni a freccia come metodi
Come già citato, le funzioni a freccia sono sconsigliate come metodi. Vediamo cosa succede quando proviamo a usarle:
'use strict';
var obj = {
i: 10,
b: () => console.log(this.i, this),
c: function() {
console.log( this.i, this)
}
}
obj.b(); // prints undefined, Window
obj.c(); // prints 10, Object {...}
Le funzioni a freccia non definiscono ("bind") il proprio this
. un altro esempio usando Object.defineProperty()
:
'use strict';
var obj = {
a: 10
};
Object.defineProperty(obj, "b", {
get: () => {
console.log(this.a, typeof this.a, this);
return this.a+10; // represents global object 'Window', therefore 'this.a' returns 'undefined'
}
});
Uso dell'operatore new
Le funzioni a freccia non possono essere usate come costruttori, ed emetteranno un errore se usate con new
.
Uso di yield
La keyword yield
non deve essere usata nel body di una funzione a freccia (eccetto quando permesso in eventuali funzioni al loro interno). Conseguentemente, le funzioni a freccia non possono essere usate come generatori.
Body della funzione
Le funzioni a freccia possono avere un "body conciso" o l'usuale "blocco body".
Nel primo caso è necessaria solo un'espressione, e il return è implicito. Nel secondo caso, devi usare esplicitamente return
.
var func = x => x * x; // concise syntax, implied "return"
var func = (x, y) => { return x + y; }; // with block body, explicit "return" needed
Restituire object literals
Tieni a mente che restituire oggetti letterali usando la sintassi concisa params => {object:literal}
non funzionerà:
var func = () => { foo: 1 }; // Calling func() returns undefined!
var func = () => { foo: function() {} }; // SyntaxError: function statement requires a name
Questo perché il codice all'interno delle parentesi graffe ({}) è processato come una sequenza di statement (i.e. foo
è trattato come un label, non come una key di un oggetto).
Tuttavia, è sufficente racchiudere l'oggetto tra parentesi tonde:
var func = () => ({ foo: 1 });
Newline
Le funzioni a freccia non possono contenere un newline tra i parametri e la freccia.
var func = ()
=> 1; // SyntaxError: expected expression, got '=>'
Ordine di parsing
La freccia in una funziona a freccia non è un'operatore, ma le funzioni a freccia hanno delle regole di parsing specifiche che interagiscono differentemente con la precedenza degli operatori, rispetto alle funzioni normali.
let callback;
callback = callback || function() {}; // ok
callback = callback || () => {}; // SyntaxError: invalid arrow-function arguments
callback = callback || (() => {}); // ok
Altri esempi
// Una funzione a freccie vuota restituisce undefined
let empty = () => {};
(() => "foobar")() // IIFE, restituisce "foobar"
var simple = a => a > 15 ? 15 : a;
simple(16); // 15
simple(10); // 10
let max = (a, b) => a > b ? a : b;
// Più semplice gestire filtering, mapping, ... di array
var arr = [5, 6, 13, 0, 1, 18, 23];
var sum = arr.reduce((a, b) => a + b); // 66
var even = arr.filter(v => v % 2 == 0); // [6, 0, 18]
var double = arr.map(v => v * 2); // [10, 12, 26, 0, 2, 36, 46]
// Le catene di promise sono più concise
promise.then(a => {
// ...
}).then(b => {
// ...
});
// le funzioni a freccia senza parametri sono più semplici da visualizzare
setTimeout( _ => {
console.log("I happen sooner");
setTimeout( _ => {
// deeper code
console.log("I happen later");
}, 1);
}, 1);
Specifiche
Specification | Status | Comment |
---|---|---|
ECMAScript 2015 (6th Edition, ECMA-262) The definition of 'Arrow Function Definitions' in that specification. |
Standard | Initial definition. |
ECMAScript Latest Draft (ECMA-262) The definition of 'Arrow Function Definitions' in that specification. |
Draft |
Compatibilità dei Browser
Feature | Chrome | Firefox (Gecko) | Edge | IE | Opera | Safari |
---|---|---|---|---|---|---|
Basic support | 45.0 | 22.0 (22.0) | (Yes) |
No support |
32 | 10.0 |
Feature | Android | Android Webview | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile | Chrome for Android |
---|---|---|---|---|---|---|---|
Basic support | No support | 45.0 | 22.0 (22.0) | No support | No support | 10.0 | 45.0 |
Note specifiche per Firefox
- L'implementazione iniziale delle funzioni a freccia in Firefox le rendeva automaticamente strict. Questo è cambiato da Firefox 24. L'uso di
"use strict";
è ora obbligatorio. - Le funzioni a freccia sono semanticamente differenti dalle non-standard expression closures aggiunte in Firefox 3 (details: JavaScript 1.8), perché expression closures non assegnano
this
in modo lessicale. - Fino a Firefox 39, un newline (
\n
) era erroneamente accettato dopo i parametri della funzione. Questo è stato risolto in conformità alle specifiche ES6 e codice come() \n => {}
emetterà unSyntaxError
in questa versione e successive.