Lokalizacja nawiasów do automatycznego wykonywania anonimowych funkcji JavaScript?


107

Niedawno porównałem bieżącą wersję json2.js z wersją, którą miałem w swoim projekcie i zauważyłem różnicę w sposobie tworzenia i wykonywania wyrażenia funkcji.

Kod używany do zawijania anonimowej funkcji w nawiasach, a następnie wykonywania jej,

(function () {
  // code here
})();

ale teraz zawija funkcję wykonywaną automatycznie w nawiasach.

(function () {
  // code here
}());

CMS zawiera komentarz w zaakceptowanej odpowiedzi składni zamkniętej funkcji anonimowej Explain JavaScript, że „oba: (function(){})();i (function(){}());są prawidłowe”.

Zastanawiałem się, jaka jest różnica? Czy te pierwsze zajmują pamięć, pozostawiając globalną, anonimową funkcję? Gdzie powinien znajdować się nawias?




1
Przeczytaj także o celu tej konstrukcji lub sprawdź wyjaśnienie ( techniczne ) (również tutaj ). Dlaczego nawiasy są konieczne, zobacz to pytanie .
— Bergi,

Odpowiedzi:


66

Są praktycznie takie same.

Pierwszy zawija funkcję w nawiasach, aby uczynić ją prawidłowym wyrażeniem i wywołuje ją. Wynik wyrażenia jest niezdefiniowany.

Drugi wykonuje funkcję, a nawiasy wokół automatycznego wywołania sprawiają, że jest to prawidłowe wyrażenie. Ocenia również jako niezdefiniowany.

Nie sądzę, aby można było to zrobić „we właściwy” sposób, ponieważ wynik wyrażenia jest taki sam.

> function(){}()
SyntaxError: Unexpected token (
> (function(){})()
undefined
> (function(){return 'foo'})()
"foo"
> (function(){ return 'foo'}())
"foo"

8
JSLint chce "(function () {} ());". JSLint mówi: „Przenieś wywołanie do parenów, które zawierają funkcję”.
— XP1

27
Właściwie nie jesteś ograniczony do tych dwóch, możesz użyć prawie wszystkiego, co sprawi, że kompilator zda sobie sprawę, że funkcja jest częścią wyrażenia, a nie instrukcji, takiej jak +function(){}()lub !function(){}().
— Tgr,

49
@ XP1: JSLint chce wielu rzeczy, które są charakterystyczne dla stylu Crockforda, a nie merytorycznych. To jest jeden z nich.
— TJ Crowder

@TJCrowder. Co byś polecił? jQuery używa pierwszego stylu, a Crockford drugiego.
— Teej

4
@ThorpeObazee: To naprawdę nie ma znaczenia, więc rób co wolisz. Polecam na niektóre z bardziej OUTRE te ( -function(){}();, !function(){}();, iw zasadzie każdy inny operator tuż przed functionteż praca, ale bym trzymać się wersji z użyciem nawiasów). Pierwszą widzę znacznie częściej niż drugą i to moje preferencje; dla mnie też ma to większy sens, ale to subiektywne. FWIW: jsbin.com/ejaqow
— TJ Crowder

13

W takim razie to nie ma znaczenia. Wywołujesz wyrażenie, które jest rozpoznawane jako funkcja z pierwszej definicji oraz definiujesz i natychmiast wywołujesz funkcję w drugim przykładzie. Są podobne, ponieważ wyrażenie funkcji w pierwszym przykładzie jest po prostu definicją funkcji.

Istnieją inne, bardziej użyteczne przypadki wywoływania wyrażeń, które dają funkcje:

(foo || bar)()

3
Dla wyjaśnienia dla innych czytelników (głównie dlatego, że sam tego na początku nie zrozumiałem :)), foo i / lub bar muszą już równać się jakiejś funkcji. (np foo = function(){alert('hi');}. jeśli żadna z nich nie jest funkcją, generowany jest błąd.
— Alexander Bird

3
@AlexanderBird Dalsze wyjaśnienie - zgłosi również błąd, jeśli foojest „prawda”, ale nie jest funkcją.
— JLRishe

9

Nie ma żadnej różnicy poza składnią.

Jeśli chodzi o twoje obawy dotyczące drugiej metody zrobienia tego:

Rozważać:

(function namedfunc () { ... }())

namedfuncnadal nie będzie w zasięgu globalnym, mimo że podałeś nazwę. To samo dotyczy funkcji anonimowych. Jedynym sposobem uzyskania go w tym zakresie byłoby przypisanie go do zmiennej wewnątrz parens.

((namedfunc = function namedfunc () { ... })())

Zewnętrzne parens są niepotrzebne:

(namedfunc = function namedfunc () { ... })()

Ale i tak nie chciałeś tej globalnej deklaracji, prawda?

A więc sprowadza się to do:

(function namedfunc () { ... })()

I możesz to jeszcze bardziej zredukować: nazwa jest niepotrzebna, ponieważ nigdy nie będzie używana (chyba że twoja funkcja jest rekurencyjna ... a nawet wtedy możesz użyć arguments.callee)

(function () { ... })()

Tak o tym myślę (może się nie zgadza, nie czytałem jeszcze specyfikacji ECMAScript). Mam nadzieję, że to pomoże.


Zauważ, że arguments.calleejest to przestarzałe od wersji ES5 (i zabronione w trybie ścisłym).
— nyuszika7h

„Zewnętrzne pareny są niepotrzebne:” - myślę, że zapobiegają błędom podczas łączenia plików, w przeciwnym razie potrzebujesz! lub coś.
— Jimmyt1988

-3

Różnica istnieje tylko dlatego, że Douglasowi Crockfordowi nie podoba się pierwszy styl IIFE ! (poważnie) Jak widać na tym filmie !! .

Jedynym powodem istnienia dodatkowego zawijania (){w obu stylach} jest pomoc w utworzeniu tej sekcji kodu Wyrażenie funkcji , ponieważ nie można natychmiast wywołać deklaracji funkcji . Niektóre skrypty / Minify-ers tylko stosowanie +, !, -i ~zamiast zbyt nawiasach. Lubię to:

+function() {  
    var foo = 'bar';  
}();

!function() {  
    var foo = 'bar';  
}();

-function() {  
    var foo = 'bar';  
}();

~function() {  
    var foo = 'bar';  
}();

A wszystko to jest dokładnie takie samo, jak twoje alternatywy. Wybór spośród tych przypadków jest całkowicie samodzielny i nie ma znaczenia. {Te, które ()produkują plik większy o 1 bajt ;-)}

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.