W jaki sposób !! ~ (nie tylda / bang bang tylda) zmienia wynik wywołania metody Array „zawiera / zawiera”?

95

Jeśli czytasz komentarze na jQuery inArraystronie tutaj , jest interesująca deklaracja:

!!~jQuery.inArray(elm, arr) 

Teraz uważam, że podwójny wykrzyknik przekonwertuje wynik na typ booleano wartości true. Nie rozumiem, jakie jest zastosowanie w tym ~wszystkim operatora tyldy ( )?

var arr = ["one", "two", "three"];
if (jQuery.inArray("one", arr) > -1) { alert("Found"); }

Refaktoryzacja ifinstrukcji:

if (!!~jQuery.inArray("one", arr)) { alert("Found"); }

Awaria:

jQuery.inArray("one", arr)     // 0
~jQuery.inArray("one", arr)    // -1 (why?)
!~jQuery.inArray("one", arr)   // false
!!~jQuery.inArray("one", arr)  // true

Zauważyłem również, że jeśli umieszczę tyldę z przodu, wynik jest -2.

~!!~jQuery.inArray("one", arr) // -2

Nie rozumiem tutaj celu tyldy. Czy ktoś może to wyjaśnić lub wskazać mi źródło informacji?

user717236
źródło
50
Ktokolwiek napisałby taki kod, musi odejść od klawiatury.
Kirk Woll,
12
@KirkWoll: Dlaczego? ~jQuery.inArray()jest w rzeczywistości bardzo przydatny - być może nawet bardzo dobry powód, dla którego funkcje wyszukiwania zwracają -1błąd (jedyna wartość, której uzupełnienie do dwóch jest fałszywe). Kiedy już zobaczysz i zrozumiesz tę sztuczkę, czuję, że jest ona jeszcze bardziej czytelna niż != -1.
Amadan
9
@Amadan - nie. Po prostu nie. Poważnie, nie mogę uwierzyć, że broniąc !!~do niczego .
Kirk Woll,
24
Problem w tym, że po prostu: „sztuczka”. Główna różnica między if (x != -1)i if (~x)dla mnie polega na tym, że to pierwsze faktycznie wyraża to, co zamierzasz zrobić. To ostatnie wyraża chęć zrobienia czegoś zupełnie innego ("proszę przekonwertować moją 64-bitową liczbę na 32-bitową liczbę całkowitą i sprawdzić, czy bitowe NIE tej liczby jest prawdziwe"), gdzie po prostu uzyskasz pożądany wynik w tym jeden przypadek.
JimmiTh
10
>= 0prawdopodobnie nie był wystarczająco jasny , więc !!~użyto bardziej tajemniczego .
Yoshi

Odpowiedzi:

56

Operator tyldy w rzeczywistości nie jest częścią jQuery - jest operatorem bitowym NOT w samym JavaScript.

Zobacz The Great Mystery of the Tilde (~) .

Otrzymujesz dziwne liczby w swoich eksperymentach, ponieważ wykonujesz bitową operację logiczną na liczbie całkowitej (która, o ile wiem, może być przechowywana jako uzupełnienie do dwóch lub coś w tym rodzaju ...)

Dopełnienie do dwóch wyjaśnia, jak przedstawić liczbę w systemie binarnym. Myślę, że miałem rację.

pglhall
źródło
3
Naprawiony! (Zmieniono to na inny link, który, co dziwne, został napisany po mojej oryginalnej odpowiedzi ...)
pglhall
121

Istnieje konkretny powód, dla którego czasami zobaczysz ~zastosowany przed$.inArray .

Gruntownie,

~$.inArray("foo", bar)

to krótszy sposób

$.inArray("foo", bar) !== -1

$.inArrayzwraca indeks pozycji w tablicy, jeśli zostanie znaleziony pierwszy argument, i zwraca -1, jeśli nie zostanie znaleziony. Oznacza to, że jeśli szukasz wartości logicznej „czy ta wartość jest w tablicy?”, Nie możesz wykonać porównania boolowskiego, ponieważ -1 jest prawdziwą wartością, a kiedy $ .inArray zwraca 0 (wartość fałszywą ), to znaczy, że faktycznie znajduje się w pierwszym elemencie tablicy.

Zastosowanie ~operatora bitowego powoduje, -1że staje się 0i powoduje, że 0 staje się `` -1. Tak więc, nie znalezienie wartości w tablicy i zastosowanie bitowego NIE skutkuje błędną wartością (0), a wszystkie inne wartości zwrócą liczby inne niż 0 i będą reprezentować prawdziwy wynik.

if (~$.inArray("foo", ["foo",2,3])) {
    // Will run
}

I będzie działać zgodnie z przeznaczeniem.

Yahel
źródło
2
Jak dobrze obsługiwane jest to w przeglądarkach (teraz w 2014 roku?) Czy było to doskonale obsługiwane przez cały czas?
Tabletki wybuchowe
zdziwiłbym się, gdyby podstawowe operacje, takie jak te, nie były doskonałe.
pcarvalho
104

!!~exprocenia, falsekiedy exprjest -1inaczej true.
To samo co expr != -1, tylko zepsute *


Działa, ponieważ operacje bitowe JavaScript konwertują operandy na 32-bitowe liczby całkowite ze znakiem w formacie dopełnienia do dwóch. Tak więc !!~-1jest oceniany w następujący sposób:

   -1 = 1111 1111 1111 1111 1111 1111 1111 1111b // two's complement representation of -1
  ~-1 = 0000 0000 0000 0000 0000 0000 0000 0000b // ~ is bitwise not (invert all bits)
   !0 = true                                     // ! is logical not (true for falsy)
!true = false                                    // duh

Wartość inna niż -1będzie miała co najmniej jeden bit ustawiony na zero; odwrócenie go stworzy prawdziwą wartość; zastosowanie!dwukrotne operatora do prawdziwej wartości zwraca wartość logiczną prawda.

W przypadku użycia z .indexOf()i chcemy tylko sprawdzić, czy wynik jest, -1czy nie:

!!~"abc".indexOf("d") // indexOf() returns -1, the expression evaluates to false
!!~"abc".indexOf("a") // indexOf() returns  0, the expression evaluates to true
!!~"abc".indexOf("b") // indexOf() returns  1, the expression evaluates to true

* !!~8589934591 zwraca wartość false, więc towstrętnie można wiarygodnie użyć do przeprowadzenia testów -1.

Salman A
źródło
1
W stabilnej bibliotece nie widzę problemu z używaniem ~foo.indexOf(bar), nie ma to znaczących oszczędności na postaci lub wydajności, ale jest to stosunkowo powszechny skrót w ten sam sposób foo = foo || {}.
zzzzBov
6
To nie jest problem ... przynajmniej dopóki ktoś nie zostanie poproszony o kontynuowanie twojego kodu.
Salman A
9
@zzzzBov, aby rozwinąć komentarz Salmana : zawsze
ahsteele
1
@ahsteele, doskonale zdaję sobie sprawę z tej zasady, jednak operatory bitowe są częścią każdego języka programowania, o którym przychodzi mi do głowy. Staram się programować w sposób czytelny dla kogoś, kto potrafi czytać kod . Nie przestaję używać funkcji języka tylko dlatego, że ktoś go nie rozumie, bo inaczej nie byłbym w stanie ich używać!! .
zzzzBov
Ściśle mówiąc, >= 0nie zachowuje się tak samo jak !!~. !== -1jest bliżej.
Peter Olson
33

~foo.indexOf(bar)jest popularnym skrótem do reprezentacji, foo.contains(bar)ponieważ containsfunkcja nie istnieje.

Zazwyczaj rzutowanie na wartości logiczne jest niepotrzebne ze względu na koncepcję „fałszywych” wartości w JavaScript. W tym przypadku służy do wymuszenia wartości wyjściowej funkcji na trueor false.

zzzzBov
źródło
6
+1 Ta odpowiedź lepiej wyjaśnia „dlaczego” niż zaakceptowana odpowiedź.
nalply
18

jQuery.inArray()zwraca -1„nie znaleziono”, którego uzupełnieniem ( ~) jest 0. Tak więc ~jQuery.inArray()zwraca fałszywą wartość ( 0) dla „nie znaleziono” i prawdziwą wartość (ujemną liczbę całkowitą) dla „znalezionego”.!!następnie sformalizuje fałsz / prawda do prawdziwej wartości logicznej false/ true. Więc !!~jQuery.inArray()poda truejako „znaleziony” i false„nie znaleziony”.

Amadan
źródło
13

Wartość ~dla wszystkich 4 bajtów intjest równa tej formule-(N+1)

WIĘC

~0   = -(0+1)   // -1
~35  = -(35+1)  // -36 
~-35 = -(-35+1) //34 
Mina Gabriel
źródło
3
Nie zawsze jest to prawdą, ponieważ (na przykład) ~2147483648 != -(2147483648 + 1).
Frxstrem
10

~Operator jest operatorem bitowym uzupełnieniem. Wynik liczby całkowitej z inArray()wynosi -1, gdy element nie został znaleziony, lub nieujemną liczbą całkowitą. Bitowe uzupełnienie -1 (reprezentowane binarnie jako wszystkie 1 bity) wynosi zero. Uzupełnienie bitowe dowolnej nieujemnej liczby całkowitej jest zawsze niezerowe.

Tak więc !!~ibędzie, truegdy liczba całkowita „i” jest nieujemną liczbą całkowitą, a falsegdy „i” jest równe dokładnie -1.

Zauważ, że ~zawsze przekształca swój operand do liczby całkowitej; to znaczy wymusza niecałkowite wartości zmiennoprzecinkowe na liczby całkowite, jak również na wartości nieliczbowe.

Spiczasty
źródło
10

Tilda jest bitowa NIE - odwraca każdy bit wartości. Zgodnie z ogólną zasadą, jeśli użyjesz ~liczby, jej znak zostanie odwrócony, a następnie odejmie się 1.

Tak więc, kiedy to robisz ~0 , otrzymasz -1 (0 odwrócone to -0, odejmij 1 to -1).

Zasadniczo jest to skomplikowany, super-mikro-zoptymalizowany sposób uzyskiwania wartości, która zawsze jest logiczna.

Joe
źródło
8

Masz rację: ten kod zwróci, falsegdy indexOfwywołanie zwróci -1; Inaczejtrue .

Jak mówisz, znacznie rozsądniej byłoby użyć czegoś takiego

return this.modifiedPaths.indexOf(path) !== -1;
LukeH
źródło
1
Ale to 3 bajty więcej do wysłania do klienta! edytuj: (przy okazji żartowałem, opublikowałem mój komentarz i zdałem sobie sprawę, że nie jest to oczywiste (co jest zarówno smutne, jak i głupie))
Wesley Murch
@Wesley: To prawda, ale trzeba to wysłać do każdego klienta tylko raz , zakładając, że klient buforuje plik .js. Powiedziawszy to, mogliby użyć >=0zamiast !==-1- żadnych dodatkowych bajtów do wysłania i nadal bardziej czytelnej niż wersja bitowa.
LukeH
2
Kto kogo tu trolluje? ;) Myślę, że wziąłem za pewnik, że pisanie czytelnego kodu jest lepsze niż tajemniczy, wstępnie zoptymalizowany kod, który generuje tego rodzaju pytania. Po prostu zminimalizuj później i napisz czytelny, zrozumiały kod teraz.
Wesley Murch
2
Osobiście powiedziałbym, że > -1jest to jeszcze bardziej czytelne, ale to prawdopodobnie bardzo subiektywne.
Yoshi
6

Plik ~Operator jest NIE bitowe operatora. Oznacza to, że przyjmuje liczbę w postaci binarnej i zamienia wszystkie zera w jedynki, a jedynki w zera.

Na przykład liczba 0 w systemie binarnym to 0000000, a -1 to 11111111. Podobnie 1 jest 00000001binarny, a -2 jest 11111110.

Frxstrem
źródło
3

Domyślam się, że jest, ponieważ jest o kilka znaków krótszy (których autorzy biblioteki zawsze szukają). Używa również operacji, które zajmują tylko kilka cykli maszynowych po skompilowaniu do kodu natywnego (w przeciwieństwie do porównania z liczbą).

Zgadzam się z inną odpowiedzią, że to przesada, ale może mieć sens w wąskiej pętli (wymaga jednak oszacowania wzrostu wydajności, w przeciwnym razie może się okazać przedwczesną optymalizacją).

Aleksandra Pawłowa
źródło
2

Zakładam, że ponieważ jest to operacja bitowa, jest to najszybszy (tani obliczeniowo) sposób sprawdzenia, czy ścieżka pojawia się w modifiedPaths.

panos2point0
źródło
1

Tak (~(-1)) === 0więc:

!!(~(-1)) === Boolean(~(-1)) === Boolean(0) === false
Inżynier
źródło
1
To może być trafne, ale czy jest to przydatne wyjaśnienie dla pytającego? Ani trochę. Gdybym nie rozumiał tego od początku, zwięzła odpowiedź nie pomogłaby.
Spudley
Myślę, że ta odpowiedź ma sens. Jeśli masz mózg matematyczny, możesz wyraźnie zobaczyć, które części zmieniają się na każdym kroku. Czy to najlepsza odpowiedź na to pytanie? Nie. Ale myślę, że to przydatne! +1
Taylor Lopez