Jaki jest cel owijania całych plików Javascript w anonimowe funkcje, takie jak " (function () { ... ) ()"?

Czytam ostatnio dużo Javascript i zauważyłem, że cały plik jest zawinięty w następujący sposób w .Pliki js do zaimportowania.

(function() {
    ... 
    code
    ...
})();

Jaki jest powód, aby to robić, a nie prosty zestaw funkcji konstruktora?

Author: Chirag Bhatia - chirag64, 2010-03-11

8 answers

Zwykle służy do przestrzeni nazw (patrz później) i kontrolowania widoczności funkcji i/lub zmiennych członkowskich. Pomyśl o tym jak o definicji obiektu. wtyczki jQuery są zwykle napisane w ten sposób.

W Javascript można zagnieżdżać funkcje. Tak więc, następujące jest legalne:

function outerFunction() {
   function innerFunction() {
      // code
   }
}

Teraz możesz wywołać outerFunction(), ale widoczność {[7] } jest ograniczona do zakresu outerFunction(), co oznacza, że jest prywatna do outerFunction(). Zasadniczo wynika z tej samej zasady co zmienne w Javascript:

var globalVariable;

function someFunction() {
   var localVariable;
}

Odpowiednio:

function globalFunction() {

   var localFunction1 = function() {
       //I'm anonymous! But localFunction1 is a reference to me!
   };

   function localFunction2() {
      //I'm named!
   }
}

W powyższym scenariuszu możesz zadzwonić globalFunction() z dowolnego miejsca, ale nie możesz zadzwonić localFunction1 lub localFunction2.

Kiedy piszesz (function() { ... code ... })(), tworzysz kod wewnątrz funkcji literalnie (co oznacza, że cały "obiekt" jest w rzeczywistości funkcją). Następnie sam wywołujesz funkcję (ostateczną ()). Więc główną zaletą tego, jak wspomniałem wcześniej, jest to, że można mieć prywatne metody / funkcje i właściwości:

(function() {
   var private_var;

   function private_function() {
     //code
   }
})()

W pierwszym przykładzie globalFunction() była publiczną funkcją, którą można wywołać, aby uzyskać dostęp do publicznej funkcji, ale w powyższym przykładzie jak ją nazwać? Funkcja samo-wywoływania powoduje, że kod jest uruchamiany automatycznie przy starcie. Tak jak możesz dodać initmystuff (); do góry dowolnego .plik js i będzie automatycznie uruchamiany jako część Globalnego zakresu, ta funkcja samo-wywoływania będzie również uruchamiana automatycznie, chociaż ponieważ jest to funkcja nienazwana nie można go wywołać wiele razy, tak jak initMyStuff ().

Fajną rzeczą jest to, że możesz również zdefiniować rzeczy wewnątrz i ujawnić je światu zewnętrznemu (przykład przestrzeni nazw, dzięki czemu możesz w zasadzie stworzyć własną bibliotekę / wtyczkę): {]}

var myPlugin = (function() {
 var private_var;

 function private_function() {
 }

 return {
    public_function1: function() {
    },
    public_function2: function() {
    }
 }
})()

Teraz możesz zadzwonić myPlugin.public_function1(), ale nie możesz uzyskać dostępu private_function()! Bardzo podobna do definicji klasy. Aby lepiej to zrozumieć, polecam następujące linki do dalszej lektury:

EDIT

Zapomniałem wspomnieć. W tym finale (), możesz przekazać wszystko, co chcesz w środku. Na przykład, kiedy tworzysz wtyczki jQuery, przekazujesz jQuery lub $ w następujący sposób:
(function(jQ) { ... code ... })(jQuery) 

Więc to, co robisz tutaj, to definiowanie funkcji, która przyjmuje jeden parametr (nazywany jQ, zmienną lokalną i znaną tylko do tej funkcji). Więc jesteś samo wywołanie funkcji i podanie parametru (zwanego również jQuery, ale ten pochodzi ze świata zewnętrznego i jest odniesieniem do samego jQuery). Nie ma potrzeby, aby to zrobić, ale są pewne zalety: {]}

  • możesz przedefiniować parametr globalny i nadać mu nazwę, która ma sens w zakresie lokalnym.
  • jest niewielka przewaga wydajności, ponieważ szybciej jest szukać rzeczy w zasięgu lokalnym, zamiast chodzić w górę łańcuch scope do globalnego zasięgu.
  • istnieją korzyści dla kompresji (minifikacji).

Wcześniej opisałem, jak te funkcje uruchamiają się automatycznie przy starcie, ale jeśli uruchamiają się automatycznie, to kto przekazuje argumenty? Technika ta zakłada, że wszystkie parametry są zdefiniowane jako zmienne globalne. Tak więc, gdyby jQuery nie było zdefiniowane jako zmienna globalna, ten przykład nie zadziałałby i nie mógłby być wywołany w inny sposób, ponieważ nasz przykład jest funkcją anonimową. As you można się domyślić, jedna rzecz jquery.js podczas jego inicjalizacji definiuje globalną zmienną 'jQuery', jak również bardziej znaną zmienną globalną'$', która pozwala na działanie tego kodu po jquery.JS jest wliczony w cenę.

 738
Author: Vivin Paliath,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-10 15:35:23

W skrócie

Podsumowanie

W najprostszej formie technika ta ma na celu zawinięcie kodu wewnątrz zakresu funkcji .

Pomaga zmniejszyć szanse na:

  • zderzenie z innymi aplikacjami / bibliotekami
  • zanieczyszczający superior (global most likely) scope

To nie wykrywa kiedy dokument jest gotowy - nie jest to jakiś rodzaj document.onload ani window.onload

Jest powszechnie znany jako Immediately Invoked Function Expression (IIFE) lub Self Executing Anonymous Function.

Objaśnienie Kodu

var someFunction = function(){ console.log('wagwan!'); };

(function() {                   /* function scope starts here */
  console.log('start of IIFE');

  var myNumber = 4;             /* number variable declaration */
  var myFunction = function(){  /* function variable declaration */
    console.log('formidable!'); 
  };
  var myObject = {              /* object variable declaration */
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
})();                           /* function scope ends */

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

W powyższym przykładzie każda zmienna zdefiniowana w funkcji (tj. zadeklarowana za pomocą var) będzie "prywatna" i dostępna tylko w ramach zakresu funkcji (jak to ujął Vivin Paliath). Innymi słowy, zmienne te nie są widoczne/osiągalne poza funkcją. Zobacz demo na żywo.

Javascript ma zakres funkcji. "Parametry i zmienne zdefiniowane w funkcji nie są widoczne poza funkcją i że zmienna zdefiniowany gdziekolwiek wewnątrz funkcji jest widoczny wszędzie wewnątrz funkcji."(z "Javascript: the Good Parts").


Więcej szczegółów

Kod Alternatywny

W końcu kod zamieszczony wcześniej może być również wykonany w następujący sposób:

var someFunction = function(){ console.log('wagwan!'); };

var myMainFunction = function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
};

myMainFunction();          // I CALL "myMainFunction" FUNCTION HERE
someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

Zobacz demo na żywo .


The Roots

Iteracja 1

Pewnego dnia ktoś prawdopodobnie pomyślał "musi być sposób, aby uniknąć nazywania 'myMainFunction' , ponieważ wszystko, czego chcemy, to jej wykonanie natychmiast."

Jeśli wrócisz do podstaw, przekonasz się, że:]}
  • expression: coś oceniającego do wartości. tj. 3+11/x
  • statement: linia(Y) kodu robi coś, ale nie, a nie ocenia do wartości. tj. if(){}

Podobnie, wyrażenia funkcyjne obliczają wartość. I jedna konsekwencja (zakładam?) jest to, że można je natychmiast wywołać:

 var italianSayinSomething = function(){ console.log('mamamia!'); }();
Tak więc nasz bardziej złożony przykład staje się:]}
var someFunction = function(){ console.log('wagwan!'); };

var myMainFunction = function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
}();

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

Zobacz też live demo .

Iteracja 2

Następnym krokiem jest myśl "dlaczego mamy var myMainFunction = jeśli nawet go nie używamy!?".

Odpowiedź jest prosta: spróbuj usunąć to, jak poniżej:

 function(){ console.log('mamamia!'); }();

Zobacz demo na żywo .

To nie zadziała, ponieważ "deklaracje funkcji nie są wywoływalne" .

Sztuczka polega na tym, że usuwając var myMainFunction = przekształciliśmy wyrażenie funkcji w deklarację funkcji. Zobacz linki w "Zasoby", aby uzyskać więcej informacji na ten temat.

Następne pytanie brzmi: "dlaczego nie mogę zachować tego jako wyrażenia funkcji z czymś innym niż var myMainFunction =?

Odpowiedź brzmi "możesz", i jest wiele sposobów, aby to zrobić: dodanie +, a !, a -, a może owinięcie w parę nawiasów (jak to teraz robi się przez konwencję), i więcej wierzę. Jako przykład:

 (function(){ console.log('mamamia!'); })(); // live demo: jsbin.com/zokuwodoco/1/edit?js,console.

Lub

 +function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wuwipiyazi/1/edit?js,console

Lub

 -function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wejupaheva/1/edit?js,console

Więc po dodaniu odpowiedniej modyfikacji do tego, co kiedyś było naszym "kodem alternatywnym", wracamy do dokładnie tego samego kodu, co użyty w przykładzie "Code Explained" [43]}

var someFunction = function(){ console.log('wagwan!'); };

(function() {
  console.log('start of IIFE');

  var myNumber = 4;
  var myFunction = function(){ console.log('formidable!'); };
  var myObject = { 
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  console.log('end of IIFE');
})();

someFunction();            // reachable, hence works: see in the console
myFunction();              // unreachable, will throw an error, see in the console
myObject.anotherFunc();    // unreachable, will throw an error, see in the console

Czytaj więcej o Expressions vs Statements:


Lunety Demistyfikacyjne

One thing one może się zastanawiać "co się stanie, gdy nie zdefiniujesz zmiennej "prawidłowo" wewnątrz funkcji - tzn. wykonasz proste zadanie zamiast tego?"

(function() {
  var myNumber = 4;             /* number variable declaration */
  var myFunction = function(){  /* function variable declaration */
    console.log('formidable!'); 
  };
  var myObject = {              /* object variable declaration */
    anotherNumber : 1001, 
    anotherFunc : function(){ console.log('formidable!'); }
  };
  myOtherFunction = function(){  /* oops, an assignment instead of a declaration */
    console.log('haha. got ya!');
  };
})();
myOtherFunction();         // reachable, hence works: see in the console
window.myOtherFunction();  // works in the browser, myOtherFunction is then in the global scope
myFunction();              // unreachable, will throw an error, see in the console

Zobacz demo na żywo .

Zasadniczo, jeśli zmiennej, która nie została zadeklarowana w bieżącym zakresie, przypisana jest wartość, to " wyszukanie łańcucha zakresu następuje, dopóki nie znajdzie zmiennej lub nie trafi w globalny zakres (w którym momencie go utworzy)".

Gdy w środowisku przeglądarki (vs środowisku serwera jak nodejs) zasięg globalny jest zdefiniowany przez obiekt window. Stąd możemy zrobić window.myOtherFunction().

Moja wskazówka "dobre praktyki" na ten temat jest zawsze używać var podczas definiowania czegokolwiek : czy jest to liczba, obiekt lub funkcja, nawet jeśli w globalnym zasięgu. Dzięki temu kod jest znacznie prostszy.

Uwaga:

  • javascript does not have block scope (Update: Block scope local variables added in ES6.)
  • javascript ma tylko function scope & global scope (window zakres w środowisku przeglądarki)

Czytaj więcej o Javascript Scopes:


Zasoby


Następne Kroki

Po pojawieniu się tej koncepcji IIFE, prowadzi ona do module pattern, co zwykle odbywa się poprzez wykorzystanie tego wzorca życia. Miłej zabawy:)

 75
Author: Adrien Be,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-11 23:41:17

Javascript w przeglądarce ma tylko kilka efektywnych zakresów: zakres funkcji i zakres globalny.

Jeśli zmienna nie znajduje się w zakresie funkcji, jest w zakresie globalnym. Zmienne globalne są ogólnie złe, więc jest to konstrukcja, która pozwala zachować zmienne biblioteki dla siebie.

 26
Author: Gareth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-11 01:25:19

To się nazywa zamknięcie. Zasadniczo zamyka kod wewnątrz funkcji, aby inne biblioteki nie ingerowały w nią. Jest to podobne do tworzenia przestrzeni nazw w skompilowanych językach.

Przykład. Załóżmy, że napiszę:

(function() {

    var x = 2;

    // do stuff with x

})();

Teraz inne biblioteki nie mogą uzyskać dostępu do zmiennej x, którą stworzyłem do użycia w mojej bibliotece.

 19
Author: Joel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-22 19:46:37

Można używać funkcji zamknięcia jako Dane w większych wyrażeniach, jak również w tej metodzie określania obsługi przeglądarek dla niektórych obiektów html5.

   navigator.html5={
     canvas: (function(){
      var dc= document.createElement('canvas');
      if(!dc.getContext) return 0;
      var c= dc.getContext('2d');
      return typeof c.fillText== 'function'? 2: 1;
     })(),
     localStorage: (function(){
      return !!window.localStorage;
     })(),
     webworkers: (function(){
      return !!window.Worker;
     })(),
     offline: (function(){
      return !!window.applicationCache;
     })()
    }
 8
Author: kennebec,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-11 02:59:11

Oprócz utrzymywania zmiennych lokalnych, bardzo przydatnym zastosowaniem jest zapisywanie biblioteki przy użyciu zmiennej globalnej, można nadać jej krótszą nazwę zmiennej do użycia 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, gdy jest wyłączony, Twój kod nadal może używać $ i nie łamać, jeśli po prostu to zrobisz:

(function($) { ...code...})(jQuery);
 7
Author: Coronus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-11 01:33:07
  1. aby uniknąć kolizji z innymi metodami/bibliotekami w tym samym oknie,
  2. unikaj zasięgu globalnego, zrób z niego zasięg lokalny,
  3. Aby przyspieszyć debugowanie (local scope),
  4. JavaScript ma tylko zakres funkcji, więc pomoże również w kompilacji kodów.
 3
Author: Vivek Mehta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-26 18:52:24

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 pokazany poniżej

(function() {
    'use strict';

    //Your code from here
})();
 1
Author: Neha Jain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-24 06:37:09