funkcja javascript prowadząca bang! składnia

204

Widziałem tę składnię w kilku bibliotekach i zastanawiam się, jaka jest z tego korzyść. (uwaga: jestem świadomy zamknięć i tego, co robi kod, martwię się tylko różnicami składniowymi)

!function(){
  // do stuff
}();

Jako alternatywa dla bardziej powszechnych

(function(){
  // do stuff
})();

do samodzielnego wywoływania anonimowych funkcji.

Zastanawiam się kilka rzeczy. Po pierwsze, co pozwala na działanie najlepszego przykładu? Dlaczego huk jest konieczny, aby to stwierdzenie było poprawne pod względem składniowym? Powiedziano mi również, że to +działa i jestem pewien, że niektórzy zamiast tego!

Po drugie, jaka jest korzyść? Wszystko, co mogę powiedzieć, to to, że ratuje jedną postać, ale nie wyobrażam sobie, że jest to tak ogromna korzyść, aby przyciągnąć wielu adopcyjnych. Czy brakuje mi innej korzyści?

Jedyną inną różnicą, jaką widzę, byłaby wartość zwracana przez funkcję wywołującą, ale w obu tych przykładach tak naprawdę nie dbamy o wartość zwracaną przez funkcję, ponieważ służy ona jedynie do utworzenia zamknięcia. Czy ktoś może mi powiedzieć, dlaczego można zastosować pierwszą składnię?

ćwiek
źródło
Frameworki lubią zapisywać jak najwięcej znaków, których minimalizator nie może zoptymalizować.
alex
Pierwsza korzyść przychodzi mi na myśl, że możesz stworzyć piaskownicę, taką jak (function ($) {...}) (jQuery);
JS Taylor
2
oba przykłady osiągają to. Jak już wspomniano, rozumiem, co robią te funkcje, bardziej interesują mnie różnice składniowe
Brad
8
Przypisuję to potrzebie, by być uroczym w sposób bardziej wyrafinowany niż poprzednia załoga. „Och, mamy tych nawiasów pogańskich!”
Jared Farrish
2
Nigdy o tym nie wiedziałem, super. Podoba mi się, !ponieważ podkreśla, że ​​jest wykonywany.
cdmckay

Odpowiedzi:

97

Idealnie powinieneś być w stanie zrobić to wszystko po prostu jako:

function(){
  // do stuff
}(); 

Oznacza to, że zadeklaruj anonimową funkcję i uruchom ją. Ale to nie zadziała ze względu na specyfikę gramatyki JS.

Najkrótszą formą osiągnięcia tego jest użycie wyrażenia np. UnaryExpression (a więc CallExpression):

!function(){
  // do stuff
}(); 

Lub dla zabawy:

-function(){
  // do stuff
}(); 

Lub:

+function(){
  // do stuff
}(); 

Lub nawet:

~function(){
  // do stuff
  return 0;
}( );
c-uśmiech
źródło
3
więc jedyna korzyść zwięzła?
Brad
12
Dodałem wszystkie powyższe opcje do testu wydajności na stronie: jsperf.com/bang-function
Shaz
5
Ekspresyjność powiedziałbym. Prędkość nie ma tak naprawdę znaczenia w takich przypadkach, że działa raz.
c-smile
1
Z ciekawości zauważyłem, że żaden huk nie działa najszybciej, ale gdzieś w ewolucji Chrome, huk stał się szybszy niż brak huku. (Prawdopodobnie nieistotne statystycznie, ale ...) Czy istnieje powód, dla którego? Nie wiem dużo o kodzie pod maską, ale uważam, że jest to dość fascynujące.
jmk2142
Dwa z twoich jednoargumentowych operatorów mogą być interpretowane jako binarne, jeśli brakuje średnika. Pierwszy i ostatni są najbezpieczniejszymi z tych przykładów.
73

W języku Javascript wiersz rozpoczynający się od functionpowinien być instrukcją funkcji i powinien wyglądać

function doSomething() {
}

Funkcja samo-wywołująca się jak

function(){
  // do stuff
}();

nie pasuje do tej formy (i spowoduje błąd składniowy przy pierwszym otwierającym paren, ponieważ nie ma nazwy funkcji), więc nawiasy służą do określenia anonimowego wyrażenia funkcji .

(function(){
  // do stuff
})();

Ale zrobi wszystko, co tworzy wyrażenie (w przeciwieństwie do instrukcji funkcji), więc stąd !. Mówi tłumaczowi, że to nie jest instrukcja funkcji. Poza tym pierwszeństwo operatora narzuca, że ​​funkcja jest wywoływana przed negacją.

Nie byłem świadomy tej konwencji, ale jeśli stanie się powszechna, może przyczynić się do jej czytelności. Chodzi mi o to, że każdy, kto czyta !functionna górze dużego bloku kodu, spodziewa się samodzielnego wywołania, tak jak jesteśmy już uwarunkowani, aby oczekiwać tego samego, kiedy widzimy (function. Tyle że stracimy te irytujące nawiasy. Spodziewałbym się, że to jest powód, w przeciwieństwie do oszczędności prędkości lub liczby postaci.

brainjam
źródło
Ta „linia zaczynająca się od funkcji jest oczekiwana ...” wygląda dość niewyraźnie. A co z tym:var foo = {CR/LF here} function bar() {}
c-smile
45

Oprócz rzeczy, które zostały już powiedziane, składnia z! jest przydatny, jeśli piszesz javascript bez średników:

var i = 1
!function(){
  console.log('ham')
}()

i = 2
(function(){
  console.log('cheese')
})()

Pierwszy przykład zwraca „ham” zgodnie z oczekiwaniami, ale drugi zgłosi błąd, ponieważ instrukcja i = 2 nie została zakończona z powodu następującego nawiasu.

Również w połączonych plikach javascript nie musisz się martwić, jeśli w poprzednim kodzie brakuje średników. Więc nie ma potrzeby wspólnego; (function () {}) (); aby upewnić się, że twoje własne się nie złamie.

Wiem, że moja odpowiedź jest trochę spóźniona, ale myślę, że jeszcze nie została wymieniona :)

Smoe
źródło
6
+1 za wskazówkę i wspomnienia ... w dzisiejszych czasach nikt nie lubi pisać javascript bez średników.
Pablo Grisafi
4
Twitter Bootstrap to wszystko JS bez średników, i jest całkiem nowy ... (przepraszam, jeśli powyższy komentarz miał charakter sarkastyczny i spóźniłem się).
brandwaffle,
2
To nie jest tak naprawdę prawda. Rozważ następujący przykład:! Function () {console.log ("ham");} ()! Function () {console.log ("cheese")} (); pojawia się błąd: „Błąd składni: nieoczekiwany token!”
heavysixer
Tak masz rację. Wystąpił błąd, jeśli umieścisz je w tej samej linii. Nie myślałem o tym. Ale do tej pory nie robi tego żaden skrypt / lib konkatenacji. A kiedy minimalizujesz i konkatujesz pliki, dodajesz średniki. Tak więc, szczerze mówiąc, nie wyobrażam sobie przypadku, w którym wpadłbyś na ten problem. Z drugiej strony jest druga w
nocy
6

Po pierwsze, jsPerf pokazuje, że używanie !(UnaryExpression) jest zwykle szybsze. Czasem wyjdzie być równe, ale jeśli tak nie jest, ja nie widziałem non-waliły jeden triumf zbytnio nad innymi jeszcze: http://jsperf.com/bang-function

Zostało to przetestowane na najnowszym Ubuntu z najstarszym (powiedzmy ..) Chrome, wersja 8. Więc wyniki mogą się oczywiście różnić.

Edycja: A może coś szalonego delete?

delete function() {
   alert("Hi!"); 
}();

czy void?

void function() {
   alert("Hi!"); 
}();
Shaz
źródło
ciekawy! Dostałem około 50/50, chociaż w Safari pod względem szybkości, niezmącona z pewnością miała swoją własną. Zastanawiam się, czy istnieje jakaś teoria potencjalnych różnic prędkości?
Brad
@brad Musi to mieć coś wspólnego z safari, jeśli spojrzysz na testy, większość innych przeglądarek wydaje się faworyzować !. Ale chciałbym też znaleźć teorię na ten temat :)
Shaz
3
Lubię void, ponieważ wyraźnie mówi „nie dbam o wartość zwracaną” i ponieważ przypomina mi konwencje w innych językach
code_monk
4

Jak widać tutaj , najlepszym sposobem na wykonywanie samodzielnie wywoływanych metod w javascript jest:

(function(){}()); -> 76,827,475 ops/sec

!function(){}();  -> 69,699,155 ops/sec

(function(){})(); -> 69,564,370 ops/sec
Geku
źródło
1
Wątpię, aby wydajność była powodem użycia jednej lub drugiej składni. Przy 70
mln operacji
3

Więc z negacją „!” i wszystkie inne jednoargumentowe operatory, takie jak +, -, ~, delete, void, wiele zostało powiedziane, aby podsumować:

!function(){
  alert("Hi!");
}(); 

Lub

void function(){
  alert("Hi!");
}();

Lub

delete function(){
  alert("Hi!");
}();

I jeszcze kilka przypadków z operatorami binarnymi dla zabawy :)

1 > function() {
   alert("Hi!"); 
}();

Lub

1 * function() {
   alert("Hi!"); 
}();

Lub

1 >>> function() {
   alert("Hi!"); 
}();

Lub nawet

1 == function() {
   alert("Hi!"); 
}();

Pozostawiając trójkę dla kogoś innego :)

Arman McHitarian
źródło
12
0?0:function() { alert("Hi!"); }();
daniel1426
Bingo, Dan! Mamy
trójkę