Ostatnio czytałem dużo skryptów Javascript i zauważyłem, że cały plik jest zawijany w następujący sposób w plikach .js do zaimportowania.
(function() {
...
code
...
})();
Jaki jest tego powód, a nie prosty zestaw funkcji konstruktora?
javascript
scope
coding-style
iife
Andrew Kou
źródło
źródło
Odpowiedzi:
Zwykle dotyczy to przestrzeni nazw (patrz później) i kontrolowania widoczności funkcji składowych i / lub zmiennych. Pomyśl o tym jak o definicji obiektu. Techniczna nazwa tego wyrażenia to Natychmiastowe wywołanie funkcji (IIFE). Wtyczki jQuery są zwykle pisane w ten sposób.
W Javascript możesz zagnieżdżać funkcje. Tak więc następujące są legalne:
Teraz możesz dzwonić
outerFunction()
, ale widocznośćinnerFunction()
jest ograniczona do zakresuouterFunction()
, co oznacza, że jest prywatnaouterFunction()
. Zasadniczo przestrzega tej samej zasady co zmienne w JavaScript:Odpowiednio:
W powyższym scenariuszu możesz dzwonić
globalFunction()
z dowolnego miejsca, ale nie możesz dzwonićlocalFunction1
lublocalFunction2
.Pisząc
(function() { ... })()
, robisz, że kod w pierwszym zestawie nawiasów jest dosłowny dla funkcji (co oznacza, że cały „obiekt” jest w rzeczywistości funkcją). Następnie wywołujesz funkcję (ostatnią()
), którą właśnie zdefiniowałeś. Główną zaletą tego, o czym wspomniałem wcześniej, jest to, że możesz mieć prywatne metody / funkcje i właściwości:W pierwszym przykładzie jawnie wywołujesz
globalFunction
według nazwy, aby go uruchomić. To znaczy, po prostu byś to zrobiłglobalFunction()
. Ale w powyższym przykładzie nie tylko definiujesz funkcję; definiujesz i wywołujesz to za jednym razem. Oznacza to, że po załadowaniu plik JavaScript jest natychmiast wykonywany. Oczywiście możesz zrobić:Zachowanie byłoby zasadniczo takie samo, z wyjątkiem jednej istotnej różnicy: unikasz zanieczyszczania zasięgu globalnego, gdy używasz IIFE (w konsekwencji oznacza to również, że nie możesz wywołać funkcji wiele razy, ponieważ nie ma ona nazwy, ale ponieważ ta funkcja ma być wykonana tylko wtedy, gdy tak naprawdę nie stanowi problemu).
Z IIFE fajną rzeczą jest to, że możesz także definiować rzeczy wewnątrz i ujawniać tylko te części, które chcesz, na zewnątrz, więc (przykład przestrzeni nazw, dzięki której możesz w zasadzie stworzyć własną bibliotekę / wtyczkę):
Teraz możesz dzwonić
myPlugin.public_function1()
, ale nie masz dostępuprivate_function()
! Bardzo podobny do definicji klasy. Aby to lepiej zrozumieć, polecam następujące linki do dalszej lektury:EDYTOWAĆ
Zapomniałem wspomnieć. W tym finale
()
możesz przekazać wszystko, co chcesz w środku. Na przykład, kiedy tworzysz wtyczki jQuery, przekazujeszjQuery
lub$
lubisz:To, co tutaj robisz, polega na zdefiniowaniu funkcji, która przyjmuje jeden parametr (nazywany
jQ
zmienną lokalną i znaną tylko tej funkcji). Następnie wywołujesz funkcję i przekazujesz parametr (nazywany takżejQuery
, ale ten pochodzi ze świata zewnętrznego i jest odniesieniem do samego jQuery). Nie ma takiej potrzeby, ale istnieją pewne zalety:Wcześniej opisałem, jak te funkcje działają automatycznie podczas uruchamiania, ale jeśli działają one automatycznie, kto przekazuje argumenty? Ta technika zakłada, że wszystkie potrzebne parametry są już zdefiniowane jako zmienne globalne. Więc jeśli jQuery nie został jeszcze zdefiniowany jako zmienna globalna, ten przykład nie działałby. Jak można się domyślić, jedną z rzeczy, które jquery.js robi podczas inicjalizacji, jest zdefiniowanie globalnej zmiennej „jQuery”, a także jej bardziej znanej zmiennej globalnej „$”, która pozwala na działanie tego kodu po uwzględnieniu jQuery.
źródło
;(function(jQ) { ... code ... })(jQuery);
ten sposób, jeśli ktoś pominął średnik w swoim skrypcie, nie złamałby twojego, szczególnie jeśli planujesz zminimalizować i połączyć swój skrypt z innym.(function (context) { ..... })(this)
co następnie pozwala ci dołączyć wszystko, co chcesz, do kontekstu nadrzędnego, odsłaniając go.W skrócie
Podsumowanie
W najprostszej formie technika ta ma na celu zawinięcie kodu w zakres funkcji .
Pomaga zmniejszyć szanse na:
To nie wykryć, gdy dokument jest gotowy - to nie jest jakaś
document.onload
aniwindow.onload
Jest powszechnie znany jako
Immediately Invoked Function Expression (IIFE)
lubSelf Executing Anonymous Function
.Kod wyjaśniony
W powyższym przykładzie każda zmienna zdefiniowana w funkcji (tj. Zadeklarowana za pomocą
var
) będzie „prywatna” i dostępna TYLKO w zakresie funkcji (jak to ujęła Vivin Paliath). Innymi słowy, te zmienne nie są widoczne / osiągalne poza funkcją. Zobacz demo na żywo .JavaScript ma zasięg funkcji. „Parametry i zmienne zdefiniowane w funkcji nie są widoczne poza funkcją, a zmienna zdefiniowana w dowolnym miejscu funkcji jest widoczna wszędzie w funkcji”. (z „Javascript: The Good Parts”).
Więcej szczegółów
Kod alternatywny
Ostatecznie kod opublikowany wcześniej można również wykonać w następujący sposób:
Zobacz demo na żywo .
Korzenie
Iteracja 1
Pewnego dnia ktoś prawdopodobnie pomyślał „musi istnieć sposób na uniknięcie nazwania„ myMainFunction ”, ponieważ wszystko, czego chcemy, to natychmiastowe wykonanie”.
Jeśli wrócisz do podstaw, przekonasz się, że:
expression
: coś wartościującego. to znaczy3+11/x
statement
: Linia (y) kodu coś robią, ale to nie nie oceniają się do wartości. to znaczyif(){}
Podobnie wyrażenia funkcyjne są wartościowane. A jedną konsekwencją (zakładam?) Jest to, że można je natychmiast wywołać:
Nasz bardziej złożony przykład to:
Zobacz demo na żywo .
Iteracja 2
Następnym krokiem jest myśl „
var myMainFunction =
po co, skoro nawet go nie używamy !?”.Odpowiedź jest prosta: spróbuj to usunąć, na przykład poniżej:
Zobacz demo na żywo .
Nie zadziała, ponieważ „deklaracje funkcji nie są wywoływalne” .
Sztuką jest to, że poprzez usunięcie
var myMainFunction =
my przekształcił wyrażenie funkcyjne w deklaracji funkcji . Więcej informacji na ten temat można znaleźć w linkach w „Zasobach”.Następne pytanie brzmi: „dlaczego nie mogę zachować tego jako wyrażenia funkcji z czymś innym niż
var myMainFunction =
?Odpowiedź brzmi „możesz”, a tak naprawdę można to zrobić na wiele sposobów: dodając a
+
, a!
, a-
lub owijanie w parę nawiasów (tak jak to obecnie odbywa się w konwencji) i więcej wierzę. Jako przykład:lub
lub
Po dodaniu odpowiedniej modyfikacji do tego, co było kiedyś naszym „Kodem alternatywnym”, wracamy do dokładnie tego samego kodu, który został użyty w przykładzie „Kod wyjaśniony”
Przeczytaj więcej o
Expressions vs Statements
:Demystifying Scopes
Można się zastanawiać: „co się stanie, gdy NIE zdefiniujesz zmiennej„ poprawnie ”w funkcji - tzn. Wykonasz proste przypisanie?
Zobacz demo na żywo .
Zasadniczo, jeśli zmienna, która nie została zadeklarowana w swoim bieżącym zakresie, ma przypisaną wartość, wówczas „wyszukiwanie łańcucha zasięgu następuje, dopóki nie znajdzie zmiennej lub nie dotrze do zasięgu globalnego (w którym momencie ją utworzy)”.
W środowisku przeglądarki (w porównaniu ze środowiskiem serwera takim jak nodejs) zasięg globalny jest definiowany przez
window
obiekt. Dlatego możemy to zrobićwindow.myOtherFunction()
.Moja wskazówka „dobrych praktyk” na ten temat polega na tym, aby zawsze używać
var
podczas definiowania czegokolwiek : niezależnie od tego, czy jest to liczba, obiekt czy funkcja, a nawet w zakresie globalnym. To znacznie upraszcza kod.Uwaga:
block scope
(Aktualizacja: zmienne lokalne zakresu bloków dodane w ES6 ).function scope
&global scope
(window
zakres w środowisku przeglądarki)Przeczytaj więcej o
Javascript Scopes
:Zasoby
Następne kroki
Po otrzymaniu tej
IIFE
koncepcji prowadzi ona do tegomodule pattern
, co zwykle wykonuje się przez wykorzystanie tego wzoru IIFE. Baw się dobrze :)źródło
JavaScript w przeglądarce ma naprawdę tylko kilka skutecznych zakresów: zakres funkcji i zasięg globalny.
Jeśli zmienna nie należy do zakresu funkcji, ma zasięg globalny. Zmienne globalne są na ogół złe, więc jest to konstrukcja pozwalająca zachować zmienne biblioteki dla siebie.
źródło
To się nazywa zamknięcie. Zasadniczo uszczelnia kod wewnątrz funkcji, aby inne biblioteki nie zakłócały go. Jest to podobne do tworzenia przestrzeni nazw w skompilowanych językach.
Przykład. Załóżmy, że piszę:
Teraz inne biblioteki nie mogą uzyskać dostępu do zmiennej,
x
którą utworzyłem w mojej bibliotece.źródło
(function(){ ... return { publicProp1: 'blah' }; })();
. Oczywiście nie idealnie równoległe do przestrzeni nazw, ale może pomóc w takim myśleniu.Możesz używać zamknięć funkcji jako danych również w większych wyrażeniach, jak również w tej metodzie określania obsługi przeglądarki dla niektórych obiektów HTML5.
źródło
Oprócz utrzymywania zmiennych lokalnych, jednym z bardzo przydatnych zastosowań jest pisanie biblioteki przy użyciu zmiennej globalnej, można nadać jej krótszą nazwę, aby można było z niej korzystać w bibliotece. Jest często używany do pisania wtyczek jQuery, ponieważ jQuery pozwala wyłączyć zmienną $ wskazującą na jQuery, używając jQuery.noConflict (). W przypadku wyłączenia kod nadal może używać $ i nie pękać, jeśli po prostu:
źródło
źródło
Powinniśmy również użyć „use strict” w funkcji scope, aby upewnić się, że kod powinien być wykonywany w „trybie ścisłym”. Przykładowy kod pokazano poniżej
źródło