Oto podsumowanie standardowych formularzy tworzących funkcje: (Pierwotnie napisane dla innego pytania, ale dostosowane po przeniesieniu do pytania kanonicznego).
Warunki:
Szybka lista:
Deklaracja funkcji
function
Wyraż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)
function
Wyraż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ę ( w
w 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.defineProperties
oraz 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 * 2
rzecz 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ż arguments
i, w stosownych przypadkach super
). Oznacza to, że this
ich wnętrze jest takie samo, jak miejsce, w this
któ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 * 2
przykł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) valueOf
za pomocą składni metody, mógłby go użyć, super.valueOf()
aby uzyskać wartość, Object.prototype.valueOf
któ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.assign
jako 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 class
skł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
.