Co wykrzyknik robi przed funkcją?


1242
!function () {}();

4
@Dykam Jego użyteczność wyjaśniono w tej odpowiedzi: stackoverflow.com/questions/5422585/...
hektorct



7
@befzz Lepiej nazywać to wyrażeniem funkcji natychmiast wywołanej, jak wyjaśnia ten artykuł później („samowykonanie” oznacza rekurencję)
Zach Esposito

Odpowiedzi:


2117

JavaScript składnia 101. Oto deklaracja funkcji :

function foo() {}

Zauważ, że nie ma średnika: jest to tylko deklaracja funkcji . Aby foo()uruchomić tę funkcję, potrzebujesz wywołania .

Teraz, kiedy dodamy pozornie nieszkodliwy wykrzyknik: !function foo() {}zamienia to w wyraz . To jest teraz wyrażenie funkcyjne .

Samo !, oczywiście, nie wywołuje funkcji, ale teraz możemy położyć ()na końcu: !function foo() {}()który ma wyższy priorytet niż !i natychmiast wywołuje funkcję.

Więc autor robi zapisywanie bajtu na wyrażenie funkcji; bardziej czytelnym sposobem pisania byłoby to:

(function(){})();

Na koniec !sprawia , że wyrażenie zwraca wartość true. To dlatego, że domyślnie wszystkie powrót Iife undefined, który pozostawia nam !undefinedco jest true. Niezbyt przydatne.


229
+1. To naprawdę najlepsza odpowiedź tutaj, i niestety, prawie nie doceniona. Oczywiście !zwraca wartość logiczną, wszyscy o tym wiemy, ale wielką zaletą jest to, że konwertuje ona również deklarację funkcji na wyrażenie funkcji, dzięki czemu funkcja może być natychmiast wywołana bez zawijania jej w nawiasach. Nie jest to oczywiste i wyraźnie intencja kodera.
gilly3

64
+1 To jedyna odpowiedź, która faktycznie odpowiada DLACZEGO chcesz to zrobić i dlaczego ktoś widzi, że użył więcej niż zaprzeczenie wyniku zwrotu, wydaje się uzasadniać. Unary operator! (także ~, - i +) jednoznacznie odróżnia się od deklaracji funkcji i pozwala parens na końcu () na wywołanie funkcji w miejscu. Jest to często wykonywane w celu utworzenia lokalnego zakresu / przestrzeni nazw dla zmiennych podczas pisania kodu modułowego.
Tom Auger

65
Kolejną korzyścią jest to! powoduje wstawienie średnika, więc niemożliwe jest nieprawidłowe połączenie tej wersji z plikiem, który nie kończy się na; Jeśli masz formularz (), uznano by to za wywołanie funkcji dowolnego elementu zdefiniowanego w poprzednim pliku. Czubek kapelusza dla mojego współpracownika.
Jure Triglav

5
@Carnix var foo =łamie dwuznaczność wyrażeń / wyrażeń i możesz po prostu pisać var foo = function(bar){}("baz");itd.
Neil,

6
Zwykle odbywa się to za pomocą skryptów minimalizacji / uglifikacji, w których liczy się każdy bajt.
Dima Slivin,

367

Funkcja:

function () {}

nic nie zwraca (lub nie jest zdefiniowany).

Czasami chcemy wywołać funkcję w trakcie jej tworzenia. Możesz mieć ochotę spróbować tego:

function () {}()

ale powoduje to SyntaxError.

Użycie !operatora przed funkcją powoduje, że jest ona traktowana jako wyrażenie, więc możemy ją nazwać:

!function () {}()

Zwróci to również wartość logiczną przeciwną do wartości zwracanej przez funkcję, w tym przypadku true, ponieważ !undefinedjest true. Jeśli chcesz, aby rzeczywista wartość zwracana była wynikiem połączenia, spróbuj to zrobić w ten sposób:

(function () {})()

28
kto może tego kiedykolwiek potrzebować?
Andrey

13
to jedyna odpowiedź, która wyjaśnia przypadek w pytaniu, brawo!
Andrey

14
Twój drugi przykładowy kod nie jest prawidłowym JavaScript. Celem tego !jest przekształcenie deklaracji funkcji w wyrażenie funkcyjne, to wszystko.
Skilldrick 30.09.11

8
@Andrey Twitter Twitter używa tego we wszystkich plikach wtyczek javascript (jQuery). Dodanie tego komentarza na wypadek, gdyby inni mogli mieć to samo pytanie.
Anmol Saraf,

2
d3.js również używa !functionskładni
Kristian

65

Warto skorzystać !z funkcji wywoływania funkcji zaznaczonej w przewodniku JavaScript airbnb

Generalnie pomysł na użycie tej techniki na osobnych plikach (czyli modułach), które później zostają połączone. Zastrzeżenie polega na tym, że pliki powinny być konkatenowane przez narzędzia, które umieszczają nowy plik w nowej linii (co zresztą jest typowe dla większości narzędzi konkat). W takim przypadku użycie !pomoże uniknąć błędu w przypadku, gdy poprzednio skonkatenowany moduł nie zaznaczył średnika końcowego, a jednak zapewni elastyczność bez problemu, aby ustawić je w dowolnej kolejności.

!function abc(){}();
!function bca(){}();

Będzie działać tak samo jak

!function abc(){}();
(function bca(){})();

ale zapisuje jedną postać i arbitralnie wygląda lepiej.

A przy okazji każdej z +, -, ~, voidoperatorzy mają ten sam efekt, jeśli chodzi o wywoływanie funkcji, na pewno, jeśli trzeba użyć czegoś do powrotu z tej funkcji będą działać inaczej.

abcval = !function abc(){return true;}() // abcval equals false
bcaval = +function bca(){return true;}() // bcaval equals 1
zyxval = -function zyx(){return true;}() // zyxval equals -1
xyzval = ~function xyz(){return true;}() // your guess?

ale jeśli używasz wzorców IIFE dla jednego pliku separacji kodu jednego modułu i używasz narzędzia concat do optymalizacji (co sprawia, że ​​zadanie dla jednego wiersza i jednego pliku), to budowa

!function abc(/*no returns*/) {}()
+function bca() {/*no returns*/}()

Wykona bezpieczne wykonanie kodu, tak samo jak pierwsza próbka kodu.

Ten wyrzuci błąd, ponieważ JavaScript ASI nie będzie w stanie wykonać swojej pracy.

!function abc(/*no returns*/) {}()
(function bca() {/*no returns*/})()

Jedna uwaga dotycząca operatorów jednoargumentowych, wykonaliby oni podobną pracę, ale tylko na wypadek, gdyby nie używali jej w pierwszym module. Nie są więc tak bezpieczne, jeśli nie masz całkowitej kontroli nad porządkiem konkatenacji.

To działa:

!function abc(/*no returns*/) {}()
^function bca() {/*no returns*/}()

To nie:

^function abc(/*no returns*/) {}()
!function bca() {/*no returns*/}()

3
W rzeczywistości te inne symbole nie mają tego samego efektu. Tak, pozwalają na wywołanie funkcji zgodnie z opisem, ale nie są identyczne. Zastanów się: var foo =! Function (bar) {console.debug (bar); }("nietoperz"); Bez względu na to, który z symboli umieścisz na wierzchu, w konsoli pojawi się „nietoperz”. Dodaj konsolę.debug („foo:”, foo); - otrzymujesz bardzo różne wyniki w zależności od tego, jakiego symbolu używasz. ! wymusza zwrócenie wartości, która nie zawsze jest pożądana. Wolę składnię ({}) () dla przejrzystości i dokładności.
Carnix

29

Zwraca, czy instrukcja może dać wartość false. na przykład:

!false      // true
!true       // false
!isValid()  // is not valid

Możesz go użyć dwa razy, aby wymusić wartość logiczną:

!!1    // true
!!0    // false

Aby bardziej bezpośrednio odpowiedzieć na twoje pytanie:

var myVar = !function(){ return false; }();  // myVar contains true

Edycja: Efektem ubocznym jest zmiana deklaracji funkcji na wyrażenie funkcji. Na przykład poniższy kod jest niepoprawny, ponieważ jest interpretowany jako deklaracja funkcji, w której brakuje wymaganego identyfikatora (lub nazwy funkcji ):

function () { return false; }();  // syntax error

6
Dla jasności dla czytelników, którzy mogą chcieć użyć przypisania z natychmiastowo wywoływaną funkcją, twój przykładowy kod var myVar = !function(){ return false; }()może pominąć !podobne var myVar = function(){ return false; }()i funkcja wykona się poprawnie, a wartość zwracana pozostanie nietknięta.
Mark Fox

1
Żeby było jasne, możesz użyć go raz, aby wymusić na Boolean, ponieważ jest to operator logiczny, a nie operator. ! 0 = prawda, a! 1 = fałsz. Dla celów minifikacji JavaScript, którą chcesz zamienić truez !0i falsez !1. Zapisuje 2 lub 3 znaki.
Triynko

9

Jest to po prostu zapisanie bajtu danych podczas minimalizacji javascript.

rozważ poniższą anonimową funkcję

function (){}

Aby uczynić powyższą funkcję samo-wywołującą, ogólnie zmienimy powyższy kod jako

(function (){}())

Teraz dodaliśmy dwa dodatkowe znaki (,)oprócz dodawania ()na końcu funkcji, które są niezbędne do wywołania funkcji. W procesie minimalizacji zwykle skupiamy się na zmniejszeniu rozmiaru pliku. Możemy więc zapisać powyższą funkcję jako

!function (){}()

Nadal oba są funkcjami samowywołającymi się, a my również oszczędzamy bajt. Zamiast 2 znaków (,)użyliśmy tylko jednego znaku!


1
Jest to pomocne, ponieważ często zobaczysz to w zminimalizowanych js
dla nazwy

5

! jest logicznym operatorem NOT , to operator logiczny, który odwróci coś na swoje przeciwieństwo.

Chociaż możesz ominąć nawiasy wywoływanej funkcji, używając BANG (!) Przed funkcją, nadal odwróci ona powrót, co może nie być tym, czego chciałeś. Podobnie jak w przypadku IEFE, zwróci wartość niezdefiniowaną , która po odwróceniu stanie się logiczną wartością prawda.

Zamiast tego użyj nawiasu zamykającego i BANG ( ! ), Jeśli to konieczne.

// I'm going to leave the closing () in all examples as invoking the function with just ! and () takes away from what's happening.

(function(){ return false; }());
=> false

!(function(){ return false; }());
=> true

!!(function(){ return false; }());
=> false

!!!(function(){ return false; }());
=> true

Inni operatorzy, którzy działają ...

+(function(){ return false; }());
=> 0

-(function(){ return false; }());
=> -0

~(function(){ return false; }());
=> -1

Połączeni operatorzy ...

+!(function(){ return false; }());
=> 1

-!(function(){ return false; }());
=> -1

!+(function(){ return false; }());
=> true

!-(function(){ return false; }());
=> true

~!(function(){ return false; }());
=> -2

~!!(function(){ return false; }());
=> -1

+~(function(){ return false; }());
+> -1

5

Wykrzyknik sprawia, że ​​każda funkcja zawsze zwraca wartość logiczną.
Ostateczna wartość jest negacją wartości zwróconej przez funkcję.

!function bool() { return false; }() // true
!function bool() { return true; }() // false

Pominięcie !w powyższych przykładach będzie składniowy .

function bool() { return true; }() // SyntaxError

Jednak lepszym sposobem na osiągnięcie tego byłoby:

(function bool() { return true; })() // true

To jest niepoprawne. !zmienia sposób, w jaki środowisko wykonawcze analizuje funkcję. Sprawia, że ​​środowisko wykonawcze traktuje funkcję jako wyrażenie funkcji (a nie deklarację). Robi to, aby umożliwić programistom natychmiastowe wywołanie funkcji przy użyciu ()składni. !zastosuje się również (tj. negacja) do wyniku wywołania wyrażenia funkcyjnego.
Ben Aston

3

Jest to inny sposób pisania IIFE (natychmiast wywoływane wyrażenie funkcyjne).

Inny sposób pisania -

(function( args ) {})()

taki sam jak

!function ( args ) {}();

Cóż, to nie jest dokładnie to samo; druga forma neguje wynik wywołania funkcji (a następnie wyrzuca go, ponieważ nie ma przypisania wartości). Wolałbym bardziej precyzyjną (function (args) {...})()składnię i pozostawienie tej !functionformy narzędziom do minimalizacji i zaciemniania.
Tobias

-1

! zaneguje (odwrotnie) wszystko, czego oczekujesz w wyniku, tj. jeśli masz

var boy = true;
undefined
boy
true
!boy
false

kiedy zadzwonisz boy, twój wynik będzie true, ale w momencie dodania !kiedy dzwonisz boy, tj. !boytwój wynik będzie false. Co innymi słowy znaczy NotBoy , ale tym razem jest to po prostu logiczna wynik, albo truealbo false.

To samo dzieje się z !function () {}();wyrażeniem, tylko uruchomienie function () {}();spowoduje oznaczenie błędu, ale dodanie !bezpośrednio przed function () {}();wyrażeniem powoduje, że jest odwrotnie niż to, function () {}();które powinno Cię zwrócić true. Przykład można zobaczyć poniżej:

function () {}();
SyntaxError: function statement requires a name
!function () {}();
true
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.