Co robi tylda, gdy poprzedza wyrażenie?

Odpowiedzi:

267

~jest bitowym operatorem, który odwraca wszystkie bity w swoim operandzie.

Na przykład, jeśli twój numer to 1, jego binarna reprezentacja liczby zmiennoprzecinkowej IEEE 754 (jak JavaScript traktuje liczby) byłaby ...

0011 1111 1111 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

~Konwertuje więc operand na 32-bitową liczbę całkowitą (robią to operatory bitowe w JavaScript) ...

0000 0000 0000 0000 0000 0000 0000 0001

Gdyby była liczbą ujemną, byłaby zapisana w uzupełnieniu 2: odwróć wszystkie bity i dodaj 1.

... a następnie odwraca wszystkie swoje części ...

1111 1111 1111 1111 1111 1111 1111 1110

Więc po co z tego pożytek? Kiedy można go kiedykolwiek użyć?

Ma kilka zastosowań. Jeśli piszesz rzeczy niskiego poziomu, jest to przydatne. Jeśli profilowałeś swoją aplikację i znalazłeś wąskie gardło, możesz zwiększyć jej wydajność, stosując bitowe sztuczki (jako jedno z możliwych narzędzi w znacznie większej torbie).

Jest to także (ogólnie) niejasny trik , aby włączyć indexOf()„y znalezione wartości zwracanej do truthy (robiąc Nie znaleziono jak falsy ) i ludzie często używają go do jej skutkiem ubocznym obcinania numery do 32 bitów (i upuszczając swoje miejsce dziesiętne przez podwojenie go, efektywnie tak samo jak Math.floor()dla liczb dodatnich).

Mówię niejasno, ponieważ nie jest od razu oczywiste, do czego jest używany. Zasadniczo chcesz, aby Twój kod komunikował się wyraźnie z innymi czytającymi go osobami. Chociaż używanie ~może wyglądać fajnie , jest na ogół zbyt sprytne dla własnego dobra. :)

Jest również mniej aktualne, ponieważ JavaScript ma Array.prototype.includes()i String.prototype.includes(). Zwracają one wartość logiczną. Jeśli platforma docelowa to obsługuje, powinieneś to preferować do testowania na obecność wartości w łańcuchu lub tablicy.

alex
źródło
2
Czy paskudne jest właściwe słowo? Jeśli to zadziała, nazwałbym to idiomem języka. Jest wiele idiomów. Gdy się ich nauczysz, nie są one niejasne. Wyjaśnienia list nie są jasne w Pythonie, jeśli ich nie znasz i można to osiągnąć za pomocą bardziej szczegółowych pętli, ale nigdy nie poprosisz programisty Python o ich nieużywanie. Podobnie value = value || defaultw JavaScript jest powszechnym i poprawnym idiomem, o ile wiesz, kiedy możesz i nie możesz go używać.
gman
3
@gman Chyba nie ma znaczenia, czy ktoś go używa, czy nie. Myślę, że porównywanie ze sobą list (funkcja językowa) nie jest tak naprawdę tym samym ( sprytny sposób na uniknięcie wpisywania dodatkowych znaków). Jeśli uważasz, że paskudny termin jest zbyt trudny, zredaguj moją odpowiedź.
alex
2
Być może bardziej powszechnym przykładem jest v = t ? a : b;. Uważam, że jest to znacznie wyraźniejsze niż var v; if (t} { v = a; } else { v = b; }zwykle w podziale na linie 5+, a także jaśniejsze niż var v = b; if (t) { v = a; }zwykle na liniach 4+. Ale znam wiele osób, które nie znają ? :operatorów, którzy wolą drugi lub trzeci sposób. Uważam, że pierwszy jest bardziej czytelny. Zgadzam się z ogólną zasadą, wyjaśniaj kod, nie używaj hacków. Wydaje mi się, że widzę ~v.indexOf('...')to bardzo wyraźnie, gdy się tego nauczę.
gman
9
Kiedy pracujesz w dużej korporacji z wieloma programistami, chcesz, aby kod był wyraźnie napisany (ANGIELSKI) i dobrze udokumentowany. Kluczem do wysokiego poziomu kodowania w języku z odśmiecaniem pamięci jest unikanie myślenia o operacjach binarnych, a wielu programistów front-end nie ma nawet doświadczenia w asemblerze pod swoimi paskami.
user2867288,
3
nie nazwałbym ~idiomatycznym. technicznie jest to część specyfikacji językowej , ale nie jest tak bardzo częścią ogólnego języka .
worc
109

Używanie go przed indexOf()wyrażeniem skutecznie daje wynik prawdziwości / fałszowania zamiast indeksu numerycznego, który jest bezpośrednio zwracany.

Jeśli wartość jest równa -1, a ~-1to 0dlatego, że -1to łańcuch wszystkich bitów 1. Każda wartość większa lub równa zero da wynik niezerowy. A zatem,

if (~someString.indexOf(something)) {
}

spowoduje uruchomienie ifkodu, gdy „coś” znajdzie się w „someString”. Jeśli spróbujesz użyć .indexOf()bezpośrednio jako wartość logiczną, to nie zadziała, ponieważ czasami zwraca zero (gdy „coś” znajduje się na początku łańcucha).

Oczywiście to też działa:

if (someString.indexOf(something) >= 0) {
}

i jest znacznie mniej tajemniczy.

Czasami zobaczysz to również:

var i = ~~something;

~Dwukrotne użycie operatora jest szybkim sposobem przekonwertowania łańcucha na 32-bitową liczbę całkowitą. Pierwszy ~dokonuje konwersji, a drugi ~odwraca bity. Oczywiście, jeśli operator zostanie zastosowany do czegoś, czego nie można przekonwertować na liczbę, otrzymasz NaNw rezultacie. ( edytuj - tak naprawdę to drugie ~jest stosowane jako pierwsze, ale masz pomysł.)

Pointy
źródło
2
Dla tych, którzy nie chcą negować krok po kroku, ~gdy wykonywane na liczbach całkowitych jest równa -(x + 1).
Fabrício Matté
Wygląda na to, no cóż, wiesz, NEGATYWNE wartości liczbowe powinny zwracać przede wszystkim ujemne wartości logiczne. Ale chyba kolejny z JS zawodzi, tak myślę?
wwaawaw
7
@adlwalrus dobrze tradycja 0bycia falsei niezerowa byt truesięga wstecz, przynajmniej do C w latach 70. i prawdopodobnie wielu innych ówczesnych języków programowania systemów. Prawdopodobnie wynika to ze sposobu działania sprzętu; wiele procesorów ustawia bit zerowy po operacji i posiada odpowiednią instrukcję rozgałęzienia, aby go przetestować.
Pointy
4
Szybszym sposobem przekonwertowania go na 32-bitową liczbę int byłby | 0, w takim przypadku jest to tylko jedna operacja.
alex
@alex rzeczywiście, chociaż niekoniecznie możemy ufać środowisku wykonawczemu, że nie interpretuje prostej aplikacji ~~dokładnie w ten sam sposób.
Pointy,
29

~Jest bitowe NOT Operator , ~xjest mniej więcej taka sama jak -(x+1). W pewnym sensie łatwiej to zrozumieć. Więc:

~2;    // -(2+1) ==> -3

Zastanów się -(x+1). -1może wykonać tę operację, aby utworzyć 0.

Innymi słowy, ~użycie z zakresem wartości liczbowych spowoduje wygenerowanie wartości fałszowania (przymusu do falseod 0) tylko dla -1wartości wejściowej, w przeciwnym razie dla każdej innej wartości prawdziwej.

Jak wiemy, -1jest powszechnie nazywany wartością wartownika . Jest on używany do wielu funkcji, które zwracają >= 0wartości do sukcesu i -1do awarii w języku C. Która ta sama zasada zwrotu wartości indexOf()w JavaScript.

W ten sposób często sprawdza się obecność / brak podłańcucha w innym ciągu

var a = "Hello Baby";

if (a.indexOf("Ba") >= 0) {
    // found it
}
if (a.indexOf("Ba") != -1) { 
    // found it
}

if (a.indexOf("aB") < 0) { 
    // not found
}
if (a.indexOf( "aB" ) == -1) { 
    // not found
}

Łatwiej byłoby to jednak zrobić, ~jak poniżej

var a = "Hello Baby";

~a.indexOf("Ba");         // -7   -> truthy
if (~a.indexOf("Ba")) {   // true
    // found it
}

~a.indexOf("aB");         // 0    -> falsy
!~a.indexOf("aB");        // true
if (!~a.indexOf( "aB" )) {  // true
    // not found
}

Nie znasz JS: Typy i gramatyka autorstwa Kyle'a Simpsona

zangw
źródło
1
Jest to zdecydowanie łatwiejsze do zrozumienia w wartości nominalnej, nawet jeśli dana osoba nie rozumie kontekst sprawia, że to działa. Spojrzałbym jeszcze raz, -(x+1)gdybym zobaczył to w oświadczeniu „if”. Tylda mówi mi dokładnie, co robi, aby zrekompensować naturę JavaScript opartą na 0. Także im mniej nawiasów, tym lepiej do czytania
Regular Joe
W początkowym bloku kodu kontrolnego można wpisać mniej, używając, if (a.indexOf("Ba") > -1) {// found} //truektóry, choć nieco dłuższy niż przykłady tyldy, jest znacznie mniejszy niż dwa podane przykłady, a dla nowych programistów var opinion = !~-1 ? 'more' : 'less'zrozumiałe.
HalfMens
24

~indexOf(item) pojawia się dość często, a odpowiedzi tutaj są świetne, ale może niektórzy ludzie muszą po prostu wiedzieć, jak go używać i „pomijać” teorię:

   if (~list.indexOf(item)) {
     // item in list
   } else {
     // item *not* in list
   }
Jorge Bucaran
źródło
1
Zgadzam się. Przewodnik po stylu Airbnb JavaScript nie zezwala, ++a --ponieważ „zachęca do nadmiernej podstępu”, a jednak jakoś ~przetrwał ( czając się
Shanimal 28.04.17
@Shanimal Alternatywą jest list.indexOf(item) >= 0lub ... > -1ponieważ javascript jest zerowy i nie zdecydował się rozwiązać tego od samego początku. Ponadto, tylko opinia (taka sama jak Airbnb), każdy, kto robi coś sensownego w javascript, wie ++i chociaż --jest mniej powszechny, znaczenie można wywnioskować.
Zwykły Joe,
@RegularJoe Zgadzam się z większością tego. Ja osobiście nie potrzebowałem ++i --od jakiegoś czasu z powodu prymitywnych metod, takich jak map, forEachitp. Chodzi mi bardziej o to, dlaczego nie uważali też za ~zbyt skomplikowane, gdy jakikolwiek używany standard obejmuje operatory zwiększania i zmniejszania. Zabraniać czegoś, więc CIS101 nie ma żadnego sensu.
Shanimal,
11

Dla tych, którzy rozważają użycie tyldy, aby stworzyć prawdziwą wartość z indexOfwyniku, jest ona bardziej wyraźna i ma mniej magii, aby zamiast tego użyć tej includesmetodyString .

'hello world'.includes('hello') //=> true
'hello world'.includes('kittens') //=> false

Pamiętaj, że jest to nowa standardowa metoda od ES 2015, więc nie będzie działać na starszych przeglądarkach. W przypadkach, w których ma to znaczenie, rozważ użycie String.prototype.includes polifill .

Ta funkcja jest również dostępna dla tablic używających tej samej składni :

['apples', 'oranges', 'cherries'].includes('apples') //=> true
['apples', 'oranges', 'cherries'].includes('unicorns') //=> false

Oto Array.prototype.inc. Zawiera polifill, jeśli potrzebujesz starszej obsługi przeglądarki.

Dana Woodman
źródło
2
Unikaj korzystania z funkcji include (). Nie jest obsługiwany w żadnej wersji IE (nie tylko starszych przeglądarkach) w momencie pisania: developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Onshop
8
Lub po prostu użyj kompilatora, abyś mógł pisać jaśniejszy kod, zamiast pisać język zgodnie z najgorszym wspólnym mianownikiem JS interpretera ...
Kyle Baker
2
@Ben ma rację, nie działa również w Netscape 4.72.
mikemaccana