Oto podsumowanie standardowych formularzy tworzących funkcje: (Pierwotnie napisane dla innego pytania, ale dostosowane po przeniesieniu do pytania kanonicznego).
Warunki:
Szybka lista:
Deklaracja funkcji
functionWyrażenie „anonimowe” (które pomimo tego terminu czasami tworzy funkcje o nazwach)
O nazwie function wyrażenie
Inicjator funkcji akcesoriów (ES5 +)
Wyrażenie funkcji strzałki (ES2015 +) (które, podobnie jak anonimowe wyrażenia funkcji, nie wymaga wyraźnej nazwy, a jednak może tworzyć funkcje o nazwach)
Deklaracja metody w inicjalizatorze obiektów (ES2015 +)
Deklaracja konstruktora i metody w class(ES2015 +)
Deklaracja funkcji
Pierwsza forma to deklaracja funkcji , która wygląda następująco:
function x() {
console.log('x');
}
Deklaracja funkcji jest deklaracją ; to nie jest stwierdzenie ani wyrażenie. W związku z tym nie podążasz za nim; (chociaż jest to nieszkodliwe).
Deklaracja funkcji jest przetwarzana, gdy wykonanie wejdzie w kontekst, w którym się pojawia, przed wykonaniem dowolnego kodu krok po kroku. Funkcja, którą tworzy, otrzymuje odpowiednią nazwę (x w powyższym przykładzie), a nazwa ta jest umieszczana w zakresie, w którym pojawia się deklaracja.
Ponieważ jest przetwarzany przed dowolnym kodem krok po kroku w tym samym kontekście, możesz wykonywać następujące czynności:
x(); // Works even though it's above the declaration
function x() {
console.log('x');
}
Aż ES2015, spec nie obejmowały co silnik JavaScript należy zrobić, jeśli umieścić deklarację funkcji wewnątrz struktury kontrolnej jak try, if, switch, while, itd., Jak poniżej:
if (someCondition) {
function foo() { // <===== HERE THERE
} // <===== BE DRAGONS
}
A ponieważ są one przetwarzane przed uruchomieniem kodu krok po kroku, trudno jest wiedzieć, co robić, gdy są w strukturze kontroli.
Chociaż nie zostało to określone do ES2015, było to dopuszczalne rozszerzenie do obsługi deklaracji funkcji w blokach. Niestety (i nieuchronnie) różne silniki robiły różne rzeczy.
Od wersji ES2015 specyfikacja mówi, co robić. W rzeczywistości daje trzy różne rzeczy do zrobienia:
- Jeśli w trybie swobodnym nie ma przeglądarki internetowej, silnik JavaScript powinien zrobić jedną rzecz
- W trybie luźnym w przeglądarce silnik JavaScript powinien zrobić coś innego
- W trybie ścisłym (przeglądarka lub nie) silnik JavaScript powinien zrobić jeszcze jedną rzecz
Reguły dla trybów swobodnych są trudne, ale w trybie ścisłym deklaracje funkcji w blokach są łatwe: są lokalne dla bloku (mają zakres bloku , który jest również nowy w ES2015) i są podnoszone na szczyt bloku. Więc:
"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)
functionWyrażenie „anonimowe”
Druga popularna forma nazywa się anonimowym wyrażeniem funkcji :
var y = function () {
console.log('y');
};
Podobnie jak wszystkie wyrażenia, jest on oceniany, gdy zostanie osiągnięty podczas wykonywania kodu krok po kroku.
W ES5 utworzona funkcja nie ma nazwy (jest anonimowa). W ES2015 funkcja ma przypisaną nazwę, jeśli to możliwe, na podstawie kontekstu. W powyższym przykładzie nazwą byłoby y. Coś podobnego robi się, gdy funkcja jest wartością inicjatora właściwości. (Aby uzyskać szczegółowe informacje o tym, kiedy to się dzieje i zasady, wyszukajSetFunctionName w specyfikacji - pojawia się wszędzie ).
Nazwanefunction wyrażenie
Trzecia postać to nazwane wyrażenie funkcyjne („NFE”):
var z = function w() {
console.log('zw')
};
Ta funkcja ma prawidłową nazwę ( ww tym przypadku). Podobnie jak wszystkie wyrażenia, jest to oceniane, gdy jest osiągane w krokowym wykonaniu kodu. Nazwa funkcji nie jest dodawana do zakresu, w którym pojawia się wyrażenie; nazywa się w zakresie samej funkcji:
var z = function w() {
console.log(typeof w); // "function"
};
console.log(typeof w); // "undefined"
Zauważ, że NFE często były źródłem błędów w implementacjach JavaScript. Na przykład IE8 i wcześniejsze obsługują NFE całkowicie niepoprawnie , tworząc dwie różne funkcje w dwóch różnych momentach. Wczesne wersje Safari również miały problemy. Dobrą wiadomością jest to, że obecne wersje przeglądarek (IE9 i nowsze, obecne Safari) nie mają już tych problemów. (Ale od tego pisania, niestety, IE8 jest nadal w powszechnym użyciu, więc używanie NFE z kodem dla Internetu jest ogólnie problematyczne.)
Inicjator funkcji akcesoriów (ES5 +)
Czasami funkcje mogą się wkraść w dużej mierze niezauważone; tak jest w przypadku funkcji akcesoriów . Oto przykład:
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"
Pamiętaj, że kiedy korzystałem z tej funkcji, nie korzystałem ()! To dlatego, że to funkcja akcesorium dla właściwości. Otrzymujemy i ustawiamy właściwość w normalny sposób, ale za kulisami funkcja jest wywoływana.
Można również tworzyć dostępowe z funkcji Object.defineProperty, Object.definePropertiesoraz mniej znanych drugiego argumentuObject.create .
Wyrażenie funkcji strzałki (ES2015 +)
ES2015 oferuje nam funkcję strzałki . Oto jeden przykład:
var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6
Zobacz, jak ta n => n * 2rzecz ukrywa się wmap() rozmowie? To jest funkcja.
Kilka rzeczy na temat funkcji strzałek:
Nie mają swoich this. Zamiast tego blisko nadthis od kontekstu, w którym są one zdefiniowane. (Zamykają się również argumentsi, w stosownych przypadkach super). Oznacza to, że thisich wnętrze jest takie samo, jak miejsce, w thisktórym zostały utworzone i nie można ich zmienić.
Jak zauważyłeś powyżej, nie używasz słowa kluczowego function; zamiast tego używasz =>.
Powyższy n => n * 2przykład jest jedną z nich. Jeśli masz wiele argumentów do przekazania funkcji, użyj parens:
var a = [1, 2, 3];
var b = a.map((n, i) => n * i);
console.log(b.join(", ")); // 0, 2, 6
(Zapamietaj to Array#map przekazuje wpis jako pierwszy argument, a indeks jako drugi).
W obu przypadkach treść funkcji jest tylko wyrażeniem; wartość zwracana przez funkcję będzie automatycznie wynikiem tego wyrażenia (nie używasz wyrażenia jawnego)return ).
Jeśli robisz więcej niż tylko jedno wyrażenie, użyj {}i jawnego return(jeśli musisz zwrócić wartość), jak zwykle:
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));
Wersja bez { ... }jest nazywana funkcją strzałki z treścią wyrażenia lub zwięzłą treścią . (Również: zwięzła funkcja strzałki.) Ta z { ... }definiowaniem ciała jest funkcją strzałki z ciałem funkcji . (Również: pełna funkcja strzałki).
Deklaracja metody w inicjalizatorze obiektów (ES2015 +)
ES2015 pozwala na krótszą formę deklarowania właściwości odwołującej się do funkcji zwanej definicją metody ; To wygląda tak:
var o = {
foo() {
}
};
prawie odpowiednikiem w ES5 i wcześniejszych byłoby:
var o = {
foo: function foo() {
}
};
różnica (inna niż gadatliwość) polega na tym, że metoda może użyć super, ale funkcja nie. Na przykład, jeśli miałbyś obiekt, który zdefiniował (powiedzmy) valueOfza pomocą składni metody, mógłby go użyć, super.valueOf()aby uzyskać wartość, Object.prototype.valueOfktórą zwróciłoby (zanim prawdopodobnie zrobiłby z nią coś innego), podczas gdy wersja ES5 musiałaby to zrobić Object.prototype.valueOf.call(this).
Oznacza to również, że metoda ma odwołanie do obiektu, na którym została zdefiniowana, więc jeśli ten obiekt jest tymczasowy (na przykład przekazujesz go Object.assignjako jeden z obiektów źródłowych), składnia metody może oznaczać, że obiekt jest zachowany w pamięci, gdy w przeciwnym razie można by go wyrzucić (jeśli silnik JavaScript nie wykryje tej sytuacji i obsłuży ją, jeśli żadna z metod nie zastosuje super).
Deklaracja konstruktora i metody w class(ES2015 +)
ES2015 zapewnia nam classskładnię, w tym deklarowane konstruktory i metody:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
getFullName() {
return this.firstName + " " + this.lastName;
}
}
Powyżej znajdują się dwie deklaracje funkcji: jedna dla konstruktora, który otrzymuje nazwę Person, i jedna dla getFullName, która jest funkcją przypisaną Person.prototype.