Podsumowanie
Czy potrafisz wyjaśnić, co kryje się za składnią enkapsulowanych funkcji anonimowych w JavaScript? Dlaczego to działa: (function(){})();
ale to nie function(){}();
:?
Co wiem
W JavaScript tworzy się nazwaną funkcję taką jak ta:
function twoPlusTwo(){
alert(2 + 2);
}
twoPlusTwo();
Możesz także utworzyć anonimową funkcję i przypisać ją do zmiennej:
var twoPlusTwo = function(){
alert(2 + 2);
};
twoPlusTwo();
Możesz obudować blok kodu, tworząc anonimową funkcję, a następnie zawijając ją w nawiasach i natychmiast wykonując:
(function(){
alert(2 + 2);
})();
Jest to przydatne podczas tworzenia skryptów modularnych, aby uniknąć zaśmiecania bieżącego zakresu lub zasięgu globalnego potencjalnie sprzecznymi zmiennymi - jak w przypadku skryptów Greasemonkey, wtyczek jQuery itp.
Teraz rozumiem, dlaczego to działa. Nawiasy zamykają zawartość i ujawniają tylko wynik (jestem pewien, że można to lepiej opisać), na przykład za pomocą (2 + 2) === 4
.
Czego nie rozumiem
Ale nie rozumiem, dlaczego to nie działa równie dobrze:
function(){
alert(2 + 2);
}();
Czy możesz mi to wyjaśnić?
źródło
Odpowiedzi:
Nie działa, ponieważ jest analizowany jako a
FunctionDeclaration
, a identyfikator nazwy deklaracji funkcji jest obowiązkowy .Gdy otaczasz go nawiasami, jest on oceniany jako a
FunctionExpression
, a wyrażenia funkcyjne można nazwać lub nie.Gramatyka
FunctionDeclaration
wygląda następująco:I
FunctionExpression
s:Jak widać token
Identifier
(Identyfikator opt )FunctionExpression
jest opcjonalny, dlatego możemy mieć wyrażenie funkcyjne bez zdefiniowanej nazwy:Lub nazwane wyrażenie funkcji:
Nawiasy (formalnie nazywane operatorem grupowania ) mogą otaczać tylko wyrażenia, a wyrażenie funkcji jest oceniane.
Dwie produkcje gramatyczne mogą być niejednoznaczne i mogą wyglądać dokładnie tak samo, na przykład:
Analizator składni wie, czy jest to a
FunctionDeclaration
lub aFunctionExpression
, w zależności od kontekstu, w którym się pojawia.W powyższym przykładzie drugi jest wyrażeniem, ponieważ operator przecinka może również obsługiwać tylko wyrażenia.
Z drugiej strony,
FunctionDeclaration
mogą faktycznie występować tylko w tak zwanymProgram
kodzie, co oznacza kod poza zakresem globalnym i wewnątrzFunctionBody
innych funkcji.Należy unikać funkcji wewnątrz bloków, ponieważ mogą one prowadzić do nieprzewidzianych zachowań, np .:
Powyższy kod powinien faktycznie wygenerować
SyntaxError
, ponieważ aBlock
może zawierać tylko instrukcje (a Specyfikacja ECMAScript nie definiuje żadnej instrukcji funkcji), ale większość implementacji jest tolerancyjna i po prostu przyjmuje drugą funkcję, tę, która zaalarmuje'false!'
.Implementacje Mozilli - Rhino, SpiderMonkey - mają inne zachowanie. Ich gramatyka zawiera niestandardowe instrukcję funkcji, co oznacza, że funkcja będzie oceniana w czasie wykonywania , a nie w czasie analizy, jak to ma miejsce w przypadku
FunctionDeclaration
s. W tych implementacjach zdefiniujemy pierwszą funkcję.Funkcje można zadeklarować na różne sposoby, porównaj następujące :
1- Funkcja zdefiniowana za pomocą konstruktora funkcji przypisanego do zmiennej zwielokrotnia :
2- Deklaracja funkcji o nazwie mnożącej się :
3- Wyrażenie funkcyjne przypisane do zmiennej zwielokrotnia się :
4- Nazwane wyrażenie funkcyjne func_name , przypisane do zmiennej pomnóż :
źródło
Chociaż jest to stare pytanie i odpowiedź, omawia temat, który do dziś rzuca wielu programistów na pętlę. Nie mogę policzyć liczby kandydatów na programistów JavaScript, z którymi przeprowadziłem wywiady, którzy nie mogliby powiedzieć mi różnicy między deklaracją funkcji a wyrażeniem funkcji i którzy nie mieli pojęcia, co to jest natychmiast wywołane wyrażenie funkcji.
Chciałbym jednak wspomnieć o jednej bardzo ważnej rzeczy, że fragment kodu Premasagar nie działałby, nawet gdyby nadał mu identyfikator nazwy.
Powodem, dla którego to nie działa, jest to, że silnik JavaScript interpretuje to jako deklarację funkcji, po której następuje całkowicie niepowiązany operator grupowania, który nie zawiera wyrażenia, a operatory grupowania muszą zawierać wyrażenie. Zgodnie z JavaScriptem powyższy fragment kodu jest równoważny z następującym.
Inną rzeczą, na którą chciałbym zwrócić uwagę, że może być użyteczny dla niektórych osób, jest to, że każdy identyfikator nazwy, który podajesz dla wyrażenia funkcji, jest prawie bezużyteczny w kontekście kodu, z wyjątkiem samej definicji funkcji.
Oczywiście używanie identyfikatorów nazw w definicjach funkcji jest zawsze pomocne, jeśli chodzi o kod debugowania, ale to coś zupełnie innego ... :-)
źródło
Świetne odpowiedzi zostały już opublikowane. Ale chcę zauważyć, że deklaracje funkcji zwracają pusty rekord zakończenia:
Fakt ten nie jest łatwy do zaobserwowania, ponieważ większość sposobów uzyskania zwróconej wartości przekształci deklarację funkcji w wyrażenie funkcyjne. Jednak
eval
pokazuje, że:Wywołanie pustego rekordu ukończenia nie ma sensu. Dlatego
function f(){}()
nie może działać. W rzeczywistości silnik JS nawet nie próbuje go wywołać, nawiasy są uważane za część innej instrukcji.Ale jeśli owiniesz funkcję w nawiasy, stanie się ona wyrażeniem funkcji:
Wyrażenia funkcyjne zwracają obiekt funkcji. A zatem można nazwać to:
(function f(){})()
.źródło
W javascript nazywa się to Expressmed-Invoked Function Expression (IIFE) .
Aby było to wyrażenie funkcyjne, musisz:
dołącz to za pomocą ()
umieść przed nim operatora pustki
przypisz ją do zmiennej.
W przeciwnym razie będzie traktowane jako definicja funkcji, a wtedy nie będzie można wywoływać / wywoływać go w tym samym czasie w następujący sposób:
Powyższe spowoduje błąd. Ponieważ możesz wywołać wyrażenie funkcji tylko natychmiast.
Można to osiągnąć na kilka sposobów: Sposób 1:
Sposób 2:
Sposób 3:
sposób 4:
Wszystkie powyższe natychmiast wywołają wyrażenie funkcji.
źródło
Mam jeszcze jedną małą uwagę. Twój kod będzie działał z niewielką zmianą:
Używam powyższej składni zamiast bardziej rozpowszechnionej wersji:
ponieważ nie udało mi się sprawić, aby wcięcie działało poprawnie dla plików javascript w vimie. Wygląda na to, że vim nie lubi nawiasów klamrowych w otwartym nawiasie.
źródło
function
słowa kluczowego jako deklarację funkcji, w którym to przypadku trailing()
jest interpretowany jako operator grupujący, który zgodnie z regułami składni JavaScript może i musi zawierać wyrażenie JavaScript.()
znajduje się operator operatora grupowania (ten, który zdecydowanie zaleca Douglas Crockford): powszechne jest używanie IIFE bez przypisywania ich do zmiennej, i łatwo zapomnieć o dołączeniu tych nawiasów, jeśli nie używasz ich konsekwentnie.Być może krótsza odpowiedź byłaby taka
jest literałem funkcji, który definiuje (anonimową) funkcję. Dodatkowa para (), interpretowana jako wyrażenie, nie jest oczekiwana na najwyższym poziomie, tylko literały.
znajduje się w wyrażeniu, które wywołuje funkcję anonimową.
źródło
Powyżej jest poprawna składnia, ponieważ wszystko przekazane w nawiasie jest traktowane jako wyrażenie funkcji.
Powyżej jest niepoprawna składnia. Ponieważ parser składni skryptu Java szuka nazwy funkcji po słowie kluczowym funkcji, ponieważ nie znajduje niczego, zgłasza błąd.
źródło
Możesz także użyć go w następujący sposób:
lub
!
- negation op konwertuje definicję fn na wyrażenie fn, dlatego można ją wywołać natychmiast za pomocą()
. Taki sam jak przy użyciu0,fn def
lubvoid fn def
źródło
Można ich używać z parametrami-argumentami takimi jak
wyniósłoby 7
źródło
Te dodatkowe nawiasy tworzą dodatkowe anonimowe funkcje między globalną przestrzenią nazw a anonimową funkcją zawierającą kod. A w funkcjach JavaScript zadeklarowanych wewnątrz innych funkcji można uzyskać dostęp tylko do przestrzeni nazw funkcji nadrzędnej, która je zawiera. Ponieważ istnieje dodatkowy obiekt (funkcja anonimowa) między zakresem globalnym a rzeczywistym zasięgiem kodu nie jest zachowywany.
źródło