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?

Kevin Hakanson
źródło

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"
meder omuraliev
źródło
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)()
Tryptyk
źródło
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.

Cristian Sanchez
źródło
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 ;-)}

behradkhodayar
źródło