Ecco il riassunto delle forme standard che creano funzioni: (Originariamente scritto per un'altra domanda, ma adattato dopo essere stato spostato nella domanda canonica.)
Condizioni:
La lista rapida:
Dichiarazione di funzione
function
Espressione "anonima" (che nonostante il termine, a volte crea funzioni con nomi)
function
Espressione nominata
Inizializzatore funzioni accessori (ES5 +)
Arrow Function Expression (ES2015 +) (che, come le espressioni di funzioni anonime, non implicano un nome esplicito e tuttavia possono creare funzioni con nomi)
Dichiarazione di metodo nell'inizializzatore di oggetti (ES2015 +)
Dichiarazioni del costruttore e del metodo in class
(ES2015 +)
Dichiarazione di funzione
Il primo modulo è una dichiarazione di funzione , che assomiglia a questo:
function x() {
console.log('x');
}
Una dichiarazione di funzione è una dichiarazione ; non è un'affermazione o espressione. Come tale, non lo segui con un ;
(anche se farlo è innocuo).
Una dichiarazione di funzione viene elaborata quando l'esecuzione entra nel contesto in cui appare, prima che venga eseguito qualsiasi codice passo-passo. Alla funzione che crea viene assegnato un nome proprio ( x
nell'esempio sopra) e quel nome viene inserito nell'ambito in cui appare la dichiarazione.
Poiché viene elaborato prima di qualsiasi codice passo-passo nello stesso contesto, puoi fare cose del genere:
x(); // Works even though it's above the declaration
function x() {
console.log('x');
}
Fino ES2015, la specifica non copriva quello che un motore JavaScript dovrebbe fare se si mette una dichiarazione di funzione all'interno di una struttura di controllo come try
, if
, switch
, while
, ecc, in questo modo:
if (someCondition) {
function foo() { // <===== HERE THERE
} // <===== BE DRAGONS
}
E poiché vengono elaborati prima dell'esecuzione del codice passo-passo, è difficile sapere cosa fare quando si trovano in una struttura di controllo.
Sebbene ciò non sia stato specificato fino a ES2015, era un'estensione consentita per supportare le dichiarazioni di funzione in blocchi. Sfortunatamente (e inevitabilmente), motori diversi hanno fatto cose diverse.
A partire da ES2015, le specifiche indicano cosa fare. In effetti, offre tre cose separate da fare:
- Se in modalità libera non su un browser Web, il motore JavaScript dovrebbe fare una cosa
- Se in modalità libera su un browser Web, il motore JavaScript dovrebbe fare qualcos'altro
- Se in modalità rigorosa (browser o meno), il motore JavaScript dovrebbe fare ancora un'altra cosa
Le regole per le modalità sciolte sono complicate, ma in modalità rigorosa , le dichiarazioni di funzione nei blocchi sono facili: sono locali per il blocco (hanno un ambito di blocco , che è anche nuovo in ES2015) e sono sollevate all'inizio del blocco. Così:
"use strict";
if (someCondition) {
foo(); // Works just fine
function foo() {
}
}
console.log(typeof foo); // "undefined" (`foo` is not in scope here
// because it's not in the same block)
function
Espressione "anonima"
La seconda forma comune si chiama espressione di funzione anonima :
var y = function () {
console.log('y');
};
Come tutte le espressioni, viene valutata quando viene raggiunta nell'esecuzione passo-passo del codice.
In ES5, la funzione creata non ha nome (è anonima). In ES2015, se possibile, alla funzione viene assegnato un nome deducendolo dal contesto. Nell'esempio sopra, il nome sarebbe y
. Qualcosa di simile viene fatto quando la funzione è il valore di un inizializzatore di proprietà. (Per i dettagli su quando questo accade e le regole, cercare SetFunctionName
nella specifica - appare in tutto il luogo.)
function
Espressione nominata
La terza forma è un'espressione di funzione denominata ("NFE"):
var z = function w() {
console.log('zw')
};
La funzione che crea ha un nome proprio ( w
in questo caso). Come tutte le espressioni, questo viene valutato quando viene raggiunto nell'esecuzione passo-passo del codice. Il nome della funzione non viene aggiunto all'ambito in cui appare l'espressione; il nome rientra nell'ambito della funzione stessa:
var z = function w() {
console.log(typeof w); // "function"
};
console.log(typeof w); // "undefined"
Si noti che le NFE sono state spesso fonte di bug per le implementazioni JavaScript. IE8 e precedenti, ad esempio, gestiscono gli NFE in modo completamente errato , creando due diverse funzioni in due momenti diversi. Anche le prime versioni di Safari avevano dei problemi. La buona notizia è che le versioni attuali dei browser (IE9 e versioni successive, Safari corrente) non presentano più tali problemi. (Ma al momento della stesura di questo documento, purtroppo, IE8 rimane ampiamente utilizzato, quindi l'utilizzo di NFE con codice per il web in generale è ancora problematico.)
Inizializzatore funzioni accessori (ES5 +)
A volte le funzioni possono insinuarsi in gran parte inosservate; questo è il caso delle funzioni di accesso . Ecco un esempio:
var obj = {
value: 0,
get f() {
return this.value;
},
set f(v) {
this.value = v;
}
};
console.log(obj.f); // 0
console.log(typeof obj.f); // "number"
Nota che quando ho usato la funzione, non l'ho usata ()
! Questo perché è una funzione di accesso per una proprietà. Otteniamo e impostiamo la proprietà in modo normale, ma dietro le quinte viene chiamata la funzione.
È inoltre possibile creare accessor funzioni con Object.defineProperty
, Object.defineProperties
e il meno noto secondo argomento Object.create
.
Espressione della funzione freccia (ES2015 +)
ES2015 ci offre la funzione freccia . Ecco un esempio:
var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6
Vedi quella n => n * 2
cosa nascosta nella map()
chiamata? Questa è una funzione.
Un paio di cose sulle funzioni delle frecce:
Non hanno il loro this
. Invece, hanno stretto sopra la this
del contesto dove sono definiti. (Si chiudono anche arguments
e, se del caso,. super
) Ciò significa che l' this
interno è uguale a quello in this
cui sono stati creati e non può essere modificato.
Come avrai notato con quanto sopra, non usi la parola chiave function
; invece, usi =>
.
L' n => n * 2
esempio sopra è una loro forma. Se hai più argomenti per passare la funzione, usi le parentesi:
var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6
(Ricorda che Array#map
passa la voce come primo argomento e l'indice come secondo.)
In entrambi i casi, il corpo della funzione è solo un'espressione; il valore restituito dalla funzione sarà automaticamente il risultato di quell'espressione (non usi un esplicito return
).
Se stai facendo qualcosa di più di una singola espressione, usa {}
ed esplicito return
(se devi restituire un valore), come di consueto:
var a = [
{first: "Joe", last: "Bloggs"},
{first: "Albert", last: "Bloggs"},
{first: "Mary", last: "Albright"}
];
a = a.sort((a, b) => {
var rv = a.last.localeCompare(b.last);
if (rv === 0) {
rv = a.first.localeCompare(b.first);
}
return rv;
});
console.log(JSON.stringify(a));
La versione senza { ... }
viene chiamata funzione freccia con un corpo di espressione o corpo conciso . (Inoltre: una funzione freccia concisa .) Quello che { ... }
definisce il corpo è una funzione freccia con un corpo funzione . (Inoltre: una funzione freccia dettagliata ).
Dichiarazione di metodo nell'inizializzatore di oggetti (ES2015 +)
ES2015 consente una forma più breve di dichiarazione di una proprietà che fa riferimento a una funzione chiamata definizione del metodo ; sembra così:
var o = {
foo() {
}
};
il quasi equivalente in ES5 e precedenti sarebbe:
var o = {
foo: function foo() {
}
};
la differenza (oltre alla verbosità) è che un metodo può usare super
, ma una funzione non può. Quindi, per esempio, se avessi un oggetto che definisse (diciamo) valueOf
usando la sintassi del metodo, avrebbe potuto usare super.valueOf()
per ottenere il valore Object.prototype.valueOf
sarebbe tornato (prima presumibilmente di fare qualcos'altro con esso), mentre invece la versione ES5 dovrebbe fare Object.prototype.valueOf.call(this)
.
Ciò significa anche che il metodo ha un riferimento all'oggetto su cui è stato definito, quindi se quell'oggetto è temporaneo (ad esempio, lo si passa Object.assign
come uno degli oggetti di origine), la sintassi del metodo potrebbe significare che l'oggetto viene mantenuto in memoria quando altrimenti avrebbe potuto essere spazzatura raccolta (se il motore JavaScript non rileva quella situazione e gestirla se nessuno dei metodi utilizza super
).
Dichiarazioni del costruttore e del metodo in class
(ES2015 +)
ES2015 ci porta la class
sintassi, inclusi costruttori e metodi dichiarati:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getFullName() {
return this.firstName + " " + this.lastName;
}
}
Esistono due dichiarazioni di funzioni sopra: una per il costruttore, che ottiene il nome Person
e una per getFullName
, a cui è assegnata una funzione Person.prototype
.