Do czego służy funkcja samo-wykonująca się w javascript?


427

Kiedy w javascript chcesz użyć tego:

(function(){
    //Bunch of code...
})();

Nad tym:

//Bunch of code...

3
Zobacz także wyjaśnienie ( techniczne ) i tutaj . Aby zapoznać się ze składnią, zobacz, dlaczego nawiasy są konieczne i dokąd powinny się udać .
Bergi,


Dlaczego ma dwa ostatnie nawiasy, tuż przed średnikiem?
Johnny

3
@johnny część przed tymi dwoma ostatnimi nawiasami deklaruje (anonimową) funkcję. Te dwa nawiasy wywołują funkcję.
Ej.

7
„Natychmiastowe wywołanie funkcji” lub IIFE to lepsza nazwa .
Flimm,

Odpowiedzi:


404

Chodzi o zmienne zakresy. Zmienne zadeklarowane w funkcji samowykonywania są domyślnie dostępne tylko dla kodu w funkcji samowykonywania. Pozwala to na pisanie kodu bez względu na to, jak zmienne są nazywane w innych blokach kodu JavaScript.

Na przykład, jak wspomniano w komentarzu Aleksandra :

(function() {
  var foo = 3;
  console.log(foo);
})();

console.log(foo);

Spowoduje to najpierw zalogowanie, 3a następnie zgłoszenie błędu do następnego, console.logponieważ foonie jest zdefiniowane.


7
A także z korzyścią dla wielu osób, w tym całej grupy inżynierów Netflix: TO TYLKO FUNKCJA. Nie jest samo w sobie reprezentatywne dla zamknięcia. Czasami auto-invokery są używane w połączeniu ze scenariuszami związanymi z zamknięciem, aby robić porządne rzeczy, ale jeśli nie widzisz czegoś, co trzymałoby się odwołania, które byłoby zbierane i usuwane w języku nie zamykającym, nie ma nic do kurwa zrobić z ZAMKNIĘCIA.
Erik Reppen

2
Więc to oznacza, że ​​jest najczęściej używany z zamknięciem?
Pir Abdul

@AlexanderBird, nie całkiem w porządku ... jeśli nie bez var, tak: ...function(){ foo=3;}? Ustawiłoby zmienną globalną, prawda?
T.Todua

2
@AlexanderBird, ale zdarza się to już w zmiennych lokalnych wewnątrz funkcji: function(){ var foo = 3; alert(foo); }; alert(foo);Więc nadal nie rozumiem
João Pimentel Ferreira

Ach, rozumiem, osiągasz wszystkie 3 cechy naraz: 1) wykonujesz funkcję tylko deklarując ją; 2) Jakakolwiek funkcja, zakres zmiennych jest tylko lokalny; oraz 3) Funkcja może być anonimowa, nie zanieczyszczając głównego zakresu
João Pimentel Ferreira,

94

Uproszczony. Tak bardzo normalnie wygląda, jest prawie pocieszające:

var userName = "Sean";

console.log(name());

function name() {
  return userName;
}

Co jednak jeśli dołączę do mojej strony naprawdę przydatną bibliotekę javascript, która tłumaczy zaawansowane znaki na ich reprezentacje na poziomie podstawowym?

Czekaj, co?

Mam na myśli, czy ktoś wpisuje znak z jakimś akcentem, ale chcę w moim programie tylko „angielskie” znaki AZ? Cóż ... hiszpańskie „ñ” i francuskie „é” można przetłumaczyć na podstawowe znaki „n” i „e”.

Więc ktoś miły napisał obszerny konwerter znaków, który mogę umieścić na mojej stronie ...

Jeden problem: ma w sobie funkcję o nazwie „nazwa” taka sama jak moja funkcja.

To się nazywa kolizja. Mamy dwie funkcje zadeklarowane w tym samym zakresie o tej samej nazwie. Chcemy tego uniknąć.

Więc musimy jakoś zawęzić nasz kod.

Jedynym sposobem na zakodowanie kodu w javascript jest zawinięcie go w funkcję:

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

To może rozwiązać nasz problem. Wszystko jest teraz zamknięte i można uzyskać do niego dostęp tylko z poziomu naszych klamr otwierających i zamykających.

Mamy funkcję w funkcji ... co jest dziwne, ale całkowicie legalne.

Tylko jeden problem. Nasz kod nie działa. Nasza zmienna userName nigdy nie jest wyświetlana w konsoli!

Możemy rozwiązać ten problem, dodając wywołanie do naszej funkcji po naszym istniejącym bloku kodu ...

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

main();

Lub przed!

main();

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

Drugi problem: jakie są szanse, że nazwa „główna” nie została jeszcze użyta? ... bardzo, bardzo szczupły.

Potrzebujemy WIĘCEJ zakresu. I jakiś sposób na automatyczne wykonanie naszej funkcji main ().

Teraz dochodzimy do funkcji automatycznego wykonywania (lub samowykonywania, samoczynnego działania, cokolwiek).

((){})();

Składnia jest niezręczna jak grzech. Jednak to działa.

Gdy zawijasz definicję funkcji w nawiasach i dołączasz listę parametrów (inny zestaw lub nawiasy!), Działa ona jak wywołanie funkcji .

Spójrzmy więc jeszcze raz na nasz kod z pewną samowystarczalną składnią:

(function main() {
  var userName = "Sean";

    console.log(name());

    function name() {
      return userName;
    }
  }
)();

Tak więc w większości samouczków, które czytasz, będziesz bombardowany terminem „anonimowy samowykonanie” lub coś podobnego.

Po wielu latach rozwoju zawodowego zdecydowanie zachęcam do nazwania każdej pisanej funkcji do celów debugowania.

Gdy coś pójdzie nie tak (i ​​tak się stanie), będziesz sprawdzać ślad w przeglądarce. Tak jest zawsze łatwiej zawęzić problemów kodu gdy wpisy w ślad stosu mają imiona!

Niezwykle długa i mam nadzieję, że to pomoże!


2
Dziękuję :) Przeszukiwałem internet dookoła, próbując zrozumieć zalety IIFE w stosunku do normalnych funkcji pod względem zmiennej prywatności, a twoja odpowiedź jest po prostu najlepsza. Wszyscy mówią, że jedną z najlepszych zalet jest to, że zmienne i funkcje wewnątrz IIFE są „nareszcie” prywatne, gdy normalna funkcja daje dokładnie to samo. Wreszcie myślę, że udało mi się to wyjaśnić. IIFE to w końcu tylko funkcja, ale teraz rozumiem, dlaczego jej używać. Dziękuję jeszcze raz!
viery365

Dziękujemy za poświęcenie czasu na tak dobre wyjaśnienie.
MSC

Niezła odpowiedź. Mam jednak pytanie dotyczące ostatniego punktu - kiedy polecasz nazwać wszystkie funkcje, czy mówisz, że istnieje sposób, aby to zrobić za pomocą funkcji wykonujących się samemu, czy sugerujesz, aby wszyscy nadali tej funkcji nazwę, a następnie ją wywołali? EDYCJA Och, rozumiem. Ten jest już nazwany. Duh. Może chciałbym wskazać, że uzasadniasz użycie nazwanej funkcji samospełniającej.
FireSBurnsmuP

Cóż, mój przyjacielu, to jest odpowiedź, której szukałem:)
João Pimentel Ferreira,

To była odpowiedź, której szukałem, a nie ta oznaczona jako zaakceptowana. Czy mam rację mówiąc, że nie chodzi o kolizję nazw zmiennych , ale o kolizję nazw funkcji ? Zmienne nawet w normalnych funkcjach nie-iife mają zasięg lokalny i nie kolidują ze zmiennymi globalnymi, prawda?
Kumar Manish

32

Samodzielne wywoływanie (znane również jako automatyczne wywoływanie) ma miejsce, gdy funkcja jest wykonywana natychmiast po jej zdefiniowaniu. Jest to podstawowy wzór i służy jako podstawa wielu innych wzorców programowania JavaScript.

Jestem wielkim fanem :) tego, ponieważ:

  • To ogranicza kod do minimum
  • Wymusza oddzielenie zachowania od prezentacji
  • Zapewnia zamknięcie, które zapobiega konfliktom nazw

Ogromnie - (Dlaczego powinieneś powiedzieć, że to dobrze?)

  • Chodzi o zdefiniowanie i wykonanie funkcji jednocześnie.
  • Może się zdarzyć, że ta samoczynnie wykonująca się funkcja zwróci wartość i przekaże funkcję jako parametr do innej funkcji.
  • Jest dobry do enkapsulacji.
  • Jest także dobry do określania zakresu bloków.
  • Tak, możesz zamknąć wszystkie swoje pliki .js w samowystarczalnej funkcji i możesz zapobiec zanieczyszczeniu globalnej przestrzeni nazw. ;)

Więcej tutaj .


43
Punkt 1. Jak? Punkt 2. To z zupełnie innej najlepszej praktyki. Punkt 3. Jaka funkcja nie działa? 4,5,6,7. Stosowność? 8. Cóż, 1/8 chyba nie jest złe.
Erik Reppen

2
Siedem lat spóźnienia, ale w punkcie 1. wcale nie redukuje kodu, w rzeczywistości dodaje co najmniej dwa wiersze kodu przy tworzeniu funkcji.
YungGun

1
Jedynym punktem tutaj jest „Zapewnia zamknięcie, które zapobiega konfliktom nazw”, co drugi punkt to przeredagowanie tego lub fałszywego. może możesz uprościć swoją odpowiedź?
pcarvalho

19

Przestrzeń nazw. Zakresy JavaScript są na poziomie funkcji.


7
wciąż przychodzą głosy negatywne, ponieważ użyłem przestrzeni nazw zamiast określania zakresu ; jest to kwestia definicji - patrz np. Wikipedia : Przestrzeń nazw w informatyce (czasami nazywana także zakresem nazw), to abstrakcyjny pojemnik lub środowisko stworzone do przechowywania logicznej grupy unikalnych identyfikatorów lub symboli (tj. nazw). oraz Identyfikator przestrzeni nazw może nadawać kontekstowi nazwę (zakres w informatyce), a terminy są czasami używane zamiennie.
Christoph

5
Zakresy na poziomie funkcji JavaScript zapewniają przestrzeń, w której żyją nazwy zmiennych, przestrzeń nazw ; że jest to anonimowy, niezwiązany z identyfikatorem przestrzeni nazw, nie ma znaczenia ...
Christoph

12

Nie mogę uwierzyć, że żadna z odpowiedzi nie wskazuje na globalne implikacje.

(function(){})()Konstrukcja nie zabezpiecza przed implikowanych globalnych, co jest dla mnie większy niepokój, zobacz http://yuiblog.com/blog/2006/06/01/global-domination/

Zasadniczo blok funkcyjny zapewnia, że ​​wszystkie zdefiniowane przez ciebie „globalne zmienne” są ograniczone do twojego programu, nie chroni cię przed zdefiniowaniem niejawnych globałów. JSHint lub podobny może zawierać zalecenia dotyczące obrony przed takim zachowaniem.

Bardziej zwięzła var App = {}składnia zapewnia podobny poziom ochrony i może być zawinięta w blok funkcyjny na stronach „publicznych”. (zobacz Ember.js lub SproutCore, aby zapoznać się z przykładami bibliotek używających tej konstrukcji)

Jeśli chodzi o privatewłaściwości, są one nieco przereklamowane, chyba że tworzysz publiczny framework lub bibliotekę, ale jeśli musisz je zaimplementować, Douglas Crockford ma kilka dobrych pomysłów.


8
Tryb ścisły chroni przed domniemanymi globalsami. To w połączeniu z auto-inwokerem zapewniłoby ci ochronę. Nigdy nie rozumiałem dudka na temat własności prywatnych. Deklaruj zmienne wewnątrz konstruktora func. Gotowy. Jeśli myśl o zapomnieniu użycia słowa kluczowego „new” nie pozwala ci zasnąć w nocy, napisz funkcję fabryczną. Zrobione ponownie.
Erik Reppen

8

Przeczytałem wszystkie odpowiedzi, brakuje mi czegoś bardzo ważnego , pocałuję się. Są dwa główne powody, dla których potrzebuję samoczynnie wykonujących się funkcji anonimowych, lub lepiej powiedzieć „ Wyrażenie funkcji natychmiast wywołane (IIFE) ”:

  1. Lepsze zarządzanie przestrzenią nazw (Unikanie zanieczyszczenia przestrzeni nazw -> Moduł JS)
  2. Zamknięcia (symulowanie członków klasy prywatnej, znane z OOP)

Pierwszy został bardzo dobrze wyjaśniony. W przypadku drugiego zapoznaj się z następującym przykładem:

var MyClosureObject = (function (){
  var MyName = 'Michael Jackson RIP';
  return {
    getMyName: function () { return MyName;},
    setMyName: function (name) { MyName = name}
  }
}());

Uwaga 1: Nie przypisujemy funkcji do MyClosureObject, co bardziej wynika z wywołania tej funkcji . Pamiętaj o tym ()w ostatnim wierszu.

Uwaga 2: Co musisz dodatkowo wiedzieć o funkcjach w JavaScript, to że funkcje wewnętrzne mają dostęp do parametrów i zmiennych funkcji, w których są one zdefiniowane.

Wypróbujmy kilka eksperymentów:

Mogę MyNamekorzystać getMyNamei działa:

 console.log(MyClosureObject.getMyName()); 
 // Michael Jackson RIP

Następujące pomysłowe podejście nie zadziałałoby:

console.log(MyClosureObject.MyName); 
// undefined

Ale mogę ustawić inną nazwę i uzyskać oczekiwany wynik:

MyClosureObject.setMyName('George Michael RIP');
console.log(MyClosureObject.getMyName()); 
// George Michael RIP

Edycja: W powyższym przykładzie MyClosureObjectjest przeznaczony do użycia bez newprefiksu, dlatego zgodnie z konwencją nie powinien być pisany wielkimi literami.


7

Czy istnieje parametr, a „Pęczek kodu” zwraca funkcję?

var a = function(x) { return function() { document.write(x); } }(something);

Zamknięcie. Wartość somethingzostaje użyta przez funkcję przypisaną do a. somethingmoże mieć pewną zmienną wartość (dla pętli) i za każdym razem ma nową funkcję.


+1; Wolę jednak jawnie var x = something;w funkcji zewnętrznej niż xparametr: imo jest bardziej czytelny w ten sposób ...
Christoph

@Christoph: Jeśli wartość „czegoś” zmieni się po utworzeniu funkcji, wówczas użyje nowej wartości, a nie tej w momencie jej tworzenia.
stesch

@stesch: skąd to masz? O ile mi wiadomo, tak nie jest; jedynym sposobem na uzyskanie prawdziwych referencji w JS jest użycie argumentu-obiektu, ale nawet to nie działa we wszystkich przeglądarkach
Christoph

@Christoph: „JavaScript: The Good Parts”, Douglas Crockford (O'Reilly)
Stesch

@stesch: nie działa tak, jak to opisujesz: nowa wartość zostanie użyta, jeśli upuścisz zmienną xi zależy bezpośrednio od zakresu leksykalnego, tj. document.write(something)...
Christoph

6

Może izolacja zakresu. Aby zmienne w deklaracji funkcji nie zanieczyszczały zewnętrznej przestrzeni nazw.

Oczywiście w połowie wdrożeń JS i tak będą.


4
Jakie byłyby to wdrożenia?
Matthew Crumley

1
Każda implementacja niepisana w trybie ścisłym i zawierająca i niejawną deklarację var, która powoduje, że jest globalna.
Erik Reppen,

5

Oto solidny przykład tego, w jaki sposób przywoływająca się anonimowa funkcja może być przydatna.

for( var i = 0; i < 10; i++ ) {
  setTimeout(function(){
    console.log(i)
  })
}

Wynik: 10, 10, 10, 10, 10...

for( var i = 0; i < 10; i++ ) {
  (function(num){
    setTimeout(function(){
      console.log(num)
    })
  })(i)
}

Wynik: 0, 1, 2, 3, 4...


czy możesz wyjaśnić nieco więcej na temat tego, co dzieje się z pierwszym zestawem kodu
radio_head

Dzięki letzamiast varpierwszym przypadku będzie w porządku.
Witalij Zdanevich,

3

Jedną różnicą jest to, że zmienne, które deklarujesz w funkcji, są lokalne, więc znikają po wyjściu z funkcji i nie powodują konfliktu z innymi zmiennymi w innym kodzie.


1

Ponieważ funkcje w JavaScript są obiektami pierwszej klasy, definiując je w ten sposób, efektywnie definiuje „klasę” podobnie jak C ++ lub C #.

Ta funkcja może definiować zmienne lokalne i zawierać w sobie funkcje. Funkcje wewnętrzne (metody instancji efektywnej) będą miały dostęp do zmiennych lokalnych (efektywnie instancji zmiennych), ale zostaną odizolowane od reszty skryptu.


1

Funkcja samodzielnego wywołania w javascript:

Wyrażenie samo-wywołujące jest wywoływane (uruchamiane) automatycznie, bez wywoływania. Wywołanie samo-wywołujące jest wywoływane zaraz po jego utworzeniu. Jest to zasadniczo wykorzystywane do unikania konfliktu nazw, a także do uzyskania enkapsulacji. Zmienne lub zadeklarowane obiekty nie są dostępne poza tą funkcją. Aby uniknąć problemów związanych z minimalizacją (filename.min), zawsze używaj własnej funkcji.


1

Funkcja samodzielnego wykonywania służy do zarządzania zakresem zmiennej.

Zakres zmiennej to region twojego programu, w którym jest zdefiniowany.

Zmienna globalna ma zasięg globalny; jest zdefiniowany wszędzie w kodzie JavaScript i można go uzyskać z dowolnego miejsca w skrypcie, nawet w twoich funkcjach. Z drugiej strony zmienne zadeklarowane w funkcji są zdefiniowane tylko w treści funkcji. Są to zmienne lokalne, mają zasięg lokalny i są dostępne tylko w ramach tej funkcji. Parametry funkcji są również liczone jako zmienne lokalne i są zdefiniowane tylko w treści funkcji.

Jak pokazano poniżej, można uzyskać dostęp do zmiennej globalnej wewnątrz funkcji, a także zauważyć, że w treści funkcji zmienna lokalna ma pierwszeństwo przed zmienną globalną o tej samej nazwie.

var globalvar = "globalvar"; // this var can be accessed anywhere within the script

function scope() {
    alert(globalvar);
    localvar = "localvar" //can only be accessed within the function scope
}

scope(); 

Zasadniczo samoczynnie wykonująca się funkcja umożliwia pisanie kodu bez względu na to, jak zmienne są nazywane w innych blokach kodu javascript.


1
(function(){
    var foo = {
        name: 'bob'
    };
    console.log(foo.name); // bob
})();
console.log(foo.name); // Reference error

W rzeczywistości powyższa funkcja będzie traktowana jako wyrażenie funkcji bez nazwy.

Głównym celem zawinięcia funkcji za pomocą zamkniętego i otwartego nawiasu jest uniknięcie zanieczyszczenia globalnej przestrzeni.

Zmienne i funkcje w wyrażeniu funkcyjnym stały się prywatne (tj.) Nie będą dostępne poza funkcją.


1

Krótka odpowiedź brzmi: aby zapobiec zanieczyszczeniu o zasięgu globalnym (lub wyższym).

IIFE (Natychmiastowe wywołanie funkcji) jest najlepszą praktyką do pisania skryptów jako wtyczek, dodatków, skryptów użytkownika lub innych skryptów, które powinny współpracować ze skryptami innych osób . Zapewnia to, że każda zdefiniowana zmienna nie wywoła niepożądanych efektów w innych skryptach.

To jest inny sposób na napisanie wyrażenia IIFE. Osobiście wolę tę następującą metodę:

void function() {
  console.log('boo!');
  // expected output: "boo!"
}();

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void

Z powyższego przykładu jasno wynika, że ​​IIFE może również wpływać na wydajność i wydajność, ponieważ funkcja, która ma być uruchomiona tylko raz zostanie wykonana raz, a następnie na stałe zrzucona w pustkę . Oznacza to, że deklaracja funkcji lub metody nie pozostaje w pamięci.


Fajnie, nie widziałem tego voidwcześniej. Lubię to.
Ej.

1

Najpierw musisz odwiedzić MDN IIFE , teraz kilka punktów na ten temat

  • jest to Wyrażenie funkcji natychmiast wywołane . Kiedy więc plik javascript został wywołany z HTMLa, funkcja ta wywołała się natychmiast.
  • Zapobiega to dostępowi do zmiennych w ramach idiomu IIFE, a także zanieczyszczeniu zasięgu globalnego.

0

Wygląda na to, że odpowiedź na to pytanie jest już gotowa, ale mimo to opublikuję swoje uwagi.

Wiem, kiedy lubię korzystać z funkcji wykonawczych.

var myObject = {
    childObject: new function(){
        // bunch of code
    },
    objVar1: <value>,
    objVar2: <value>
}

Ta funkcja pozwala mi użyć dodatkowego kodu do zdefiniowania atrybutów i właściwości childObjects dla czystszego kodu, takich jak ustawianie często używanych zmiennych lub wykonywanie równań matematycznych; O! lub sprawdzanie błędów. w przeciwieństwie do ograniczania się do składni tworzenia instancji obiektów zagnieżdżonych ...

object: {
    childObject: {
        childObject: {<value>, <value>, <value>}
    }, 
    objVar1: <value>,
    objVar2: <value>
}

Kodowanie ma wiele niejasnych sposobów na zrobienie wielu tych samych rzeczy, przez co zastanawiasz się: „Po co zawracać sobie głowę?”. Ale pojawiają się nowe sytuacje, w których nie można już polegać tylko na podstawowych / podstawowych zleceniach.


0

Biorąc pod uwagę twoje proste pytanie: „W javascript, kiedy chcesz użyć tego: ...”

Lubię @ken_browning i @ sean_holding odpowiedzi, ale oto inny przypadek użycia, o którym nie widzę wspomnianych:

let red_tree = new Node(10);

(async function () {
    for (let i = 0; i < 1000; i++) {
        await red_tree.insert(i);
    }
})();

console.log('----->red_tree.printInOrder():', red_tree.printInOrder());

gdzie Node.insert to asynchroniczna akcja.

Nie mogę po prostu wywołać funkcji oczekiwania bez słowa kluczowego async w deklaracji mojej funkcji i nie potrzebuję nazwanej funkcji do późniejszego użycia, ale muszę czekać na to wywołanie wstawienia lub potrzebuję innych bogatszych funkcji (kto wie?) .


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.