Funzioni a freccia

Questa traduzione è incompleta. Collabora alla traduzione di questo articolo dall’originale in lingua inglese.

Una funzione a freccia ha una sintassi più compatta rispetto alla notazione a funzione e non associa i propri thisargumentssuper, or 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" on hacks.mozilla.org.

L'introduzione delle funzioni a freccia è stata influenzata da 2 fattori: sintassi più compatta e la non associazione di this.

Funzioni più corte

In alcuni pattern, è meglio avere funzioni più corte. Per comparazione:

var a = [
  "Hydrogen",
  "Helium",
  "Lithium",
  "Beryllium"
];

var a2 = a.map(function(s){ return s.length });

var a3 = a.map( s => s.length );

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  argumentsPerciò, 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 2017 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à un SyntaxError in questa versione e successive.

Vedi anche

Tag del documento e collaboratori

 Hanno collaborato alla realizzazione di questa pagina: DrJest
 Ultima modifica di: DrJest,