Bagaimana !! ~ (bukan tilde / bang bang tilde) mengubah hasil dari panggilan metode Array 'berisi / disertakan'?

95

Jika Anda membaca komentar di inArrayhalaman jQuery di sini , ada deklarasi yang menarik:

!!~jQuery.inArray(elm, arr) 

Sekarang, saya yakin tanda seru ganda akan mengubah hasil menjadi tipe boolean, dengan nilai true. Yang tidak saya mengerti adalah apa gunanya ~operator tilde ( ) dalam semua ini?

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

Refactoring ifpernyataan:

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

Kerusakan:

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

Saya juga memperhatikan bahwa jika saya meletakkan tilde di depan, hasilnya adalah -2.

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

Saya tidak mengerti tujuan tilde di sini. Bisakah seseorang menjelaskan atau mengarahkan saya ke sumber daya?

pengguna717236
sumber
50
Siapa pun yang akan menulis kode seperti itu harus menjauh dari keyboard.
Kirk Woll
12
@KirkW: Mengapa? ~jQuery.inArray()sebenarnya sangat berguna - bahkan mungkin alasan yang sangat bagus mengapa fungsi pencarian kembali -1gagal (satu-satunya nilai yang keduanya saling melengkapi salah). Setelah Anda melihat dan memahami triknya, saya merasa ini lebih mudah dibaca daripada != -1.
Amadan
9
@Amadan - no. Tidak. Serius, saya tidak bisa percaya kau membela !!~untuk apa-apa .
Kirk Woll
24
Masalahnya, hanya saja: "Trik". Perbedaan utama antara if (x != -1)dan if (~x)bagi saya, yang pertama sebenarnya mengungkapkan apa yang ingin Anda lakukan. Yang terakhir mengungkapkan Anda ingin melakukan sesuatu yang lain sepenuhnya ("tolong ubah 64-bit Number saya menjadi integer 32-bit, dan periksa apakah bitwise NOT dari integer itu benar"), di mana Anda kebetulan mendapatkan hasil yang diinginkan dalam hal ini satu kasus.
JimmiTh
10
>= 0mungkin tidak cukup leet , jadi yang lebih samar !!~digunakan.
Yoshi

Jawaban:

56

Operator tilde sebenarnya bukan bagian dari jQuery - ini bukan operator bitwise di JavaScript itu sendiri.

Lihat The Great Mystery of the Tilde (~) .

Anda mendapatkan angka-angka aneh dalam percobaan Anda karena Anda melakukan operasi logis bitwise pada integer (yang, sejauh yang saya tahu, dapat disimpan sebagai pelengkap dua atau sesuatu seperti itu ...)

Komplemen dua menjelaskan bagaimana merepresentasikan bilangan dalam biner. Saya pikir saya benar.

pglhall.dll
sumber
3
Tetap! (Menggantinya ke tautan lain yang, cukup aneh, ditulis setelah jawaban asli saya ...)
pglhall
121

Ada alasan spesifik yang terkadang Anda lihat ~diterapkan di depan $.inArray.

Pada dasarnya,

~$.inArray("foo", bar)

adalah cara yang lebih singkat untuk dilakukan

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

$.inArraymengembalikan indeks item dalam larik jika argumen pertama ditemukan, dan mengembalikan -1 jika tidak ditemukan. Ini berarti bahwa jika Anda mencari boolean "apakah nilai ini dalam array?", Anda tidak dapat melakukan perbandingan boolean, karena -1 adalah nilai yang benar, dan saat $ .inArray mengembalikan 0 (nilai yang salah ), artinya sebenarnya ditemukan di elemen pertama dari array.

Menerapkan ~operator bitwise menyebabkan -1menjadi 0, dan menyebabkan 0 menjadi `-1. Jadi, tidak menemukan nilai dalam larik dan menerapkan bitwise NOT akan menghasilkan nilai yang salah (0), dan semua nilai lainnya akan mengembalikan angka bukan-0, dan akan mewakili hasil yang benar.

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

Dan itu akan bekerja sebagaimana mestinya.

Yahel
sumber
2
Seberapa baik dukungan ini di browser (sekarang di 2014?) Atau apakah didukung dengan sempurna selama ini?
Pil Ledakan
Saya akan terkejut jika operasi dasar seperti ini tidak akan sempurna.
pcarvalho
104

!!~exprmengevaluasi falsesaat exprini -1sebaliknya true.
Sama seperti expr != -1, hanya rusak *


Ini berfungsi karena operasi bitwise JavaScript mengubah operan menjadi integer bertanda 32-bit dalam format komplemen dua. Dengan demikian !!~-1dievaluasi sebagai berikut:

   -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

Nilai selain -1akan memiliki setidaknya satu bit disetel ke nol; membalikkannya akan menciptakan nilai kebenaran; menerapkan !operator dua kali ke nilai yang benar mengembalikan boolean true.

Ketika digunakan dengan .indexOf()dan kami hanya ingin memeriksa apakah hasilnya -1atau tidak:

!!~"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

* !!~8589934591Dievaluasi ke false jadi inikekejiantidak dapat digunakan secara andal untuk menguji -1.

Salman A
sumber
1
Dalam perpustakaan yang stabil, saya tidak melihat masalah dengan penggunaan ~foo.indexOf(bar), ini bukan penghematan yang signifikan pada karakter atau kinerja, tetapi ini adalah singkatan yang relatif umum dengan cara yang foo = foo || {}sama.
zzzzBov
6
Ini bukan masalah ... setidaknya sampai orang lain diminta untuk melanjutkan dengan kode Anda.
Salman A
1
@ahsteele, saya sangat memahami aturan itu, namun operator bitwise adalah bagian dari setiap bahasa pemrograman yang dapat saya pikirkan. Saya mencoba memprogram dengan cara yang dapat dibaca oleh seseorang yang dapat membaca kode . Saya tidak berhenti menggunakan fitur suatu bahasa hanya karena orang lain tidak memahaminya, jika tidak, saya bahkan tidak dapat menggunakannya!! .
zzzzBov
Sebenarnya, >= 0tidak memiliki perilaku yang sama dengan !!~. !== -1lebih dekat.
Peter Olson
33

~foo.indexOf(bar)adalah singkatan umum untuk diwakili foo.contains(bar)karena containsfungsinya tidak ada.

Biasanya cast ke boolean tidak diperlukan karena konsep JavaScript tentang nilai "salah". Dalam hal ini digunakan untuk memaksa keluaran fungsi menjadi trueatau false.

zzzzBov
sumber
6
+1 Jawaban ini menjelaskan "mengapa" lebih baik daripada jawaban yang diterima.
nalply
18

jQuery.inArray()kembali -1untuk "tidak ditemukan", yang komplemennya ( ~) adalah 0. Jadi, ~jQuery.inArray()mengembalikan nilai salah ( 0) untuk "tidak ditemukan", dan nilai kebenaran (bilangan bulat negatif) untuk "ditemukan". !!kemudian akan memformalkan falsy / truthy menjadi boolean false/ true. Jadi, !!~jQuery.inArray()akan diberikan trueuntuk "ditemukan" dan falseuntuk "tidak ditemukan".

Amadan
sumber
13

Untuk ~semua 4 byte intsama dengan rumus ini-(N+1)

BEGITU

~0   = -(0+1)   // -1
~35  = -(35+1)  // -36 
~-35 = -(-35+1) //34 
Mina Gabriel
sumber
3
Ini tidak selalu benar, karena (misalnya) ~2147483648 != -(2147483648 + 1).
Frxstrem
10

The ~operator adalah operator bitwise pelengkap. Hasil integer dari inArray()adalah -1, jika elemen tidak ditemukan, atau beberapa integer non-negatif. Pelengkap bitwise -1 (direpresentasikan dalam biner karena semua 1 bit) adalah nol. Pelengkap bitwise dari bilangan bulat non-negatif selalu bukan nol.

Jadi, !!~iakan menjadi truesaat bilangan bulat "i" adalah bilangan bulat non-negatif, dan falsebila "i" tepat -1.

Perhatikan bahwa ~selalu memaksa operannya menjadi integer; yaitu, ini memaksa nilai floating point non-integer menjadi integer, serta nilai non-numerik.

Runcing
sumber
10

Tilde TIDAK bitwise - itu membalikkan setiap bit nilai. Sebagai pedoman umum, jika Anda menggunakan ~angka, tandanya akan dibalik, kemudian 1 akan dikurangi.

Jadi, jika Anda melakukannya ~0, Anda mendapatkan -1 (0 terbalik adalah -0, dikurangi 1 adalah -1).

Ini pada dasarnya adalah cara yang rumit dan sangat mikro-dioptimalkan untuk mendapatkan nilai yang selalu Boolean.

Joe
sumber
8

Anda benar: Kode ini akan kembali falseketika indexOfpanggilan kembali -1; sebaliknya true.

Seperti yang Anda katakan, akan jauh lebih masuk akal menggunakan sesuatu seperti

return this.modifiedPaths.indexOf(path) !== -1;
LukeH
sumber
1
Tapi itu 3 byte lagi untuk dikirim ke klien! edit: (hanya bercanda, memposting komentar saya dan menyadari bahwa itu tidak jelas (yang menyedihkan dan konyol))
Wesley Murch
@Wesley: Itu benar, tetapi hanya harus dikirim ke setiap klien satu kali , dengan asumsi klien akan menyimpan cache file .js. Karena itu, mereka dapat menggunakan >=0daripada !==-1- tidak ada byte tambahan untuk dikirim dan masih lebih mudah dibaca daripada versi bit-twiddling.
LukeH
2
Siapa yang mengerjai siapa di sini? ;) Saya rasa saya menerima begitu saja bahwa menulis kode yang dapat dibaca lebih baik daripada kode yang telah dioptimalkan sebelumnya yang menghasilkan pertanyaan semacam ini. Minimalkan nanti dan tulis kode yang dapat dibaca dan dimengerti sekarang.
Wesley Murch
2
Secara pribadi saya akan mengatakan itu > -1bahkan lebih mudah dibaca, tapi itu mungkin sangat subjektif.
Yoshi
6

Itu ~ Operator adalah bitwise TIDAK operator. Artinya, ia mengambil bilangan dalam bentuk biner dan mengubah semua nol menjadi satu dan satu menjadi nol.

Misalnya, angka 0 dalam biner adalah 0000000, sedangkan -1 adalah 11111111. Demikian juga, 1 00000001dalam biner, sedangkan -2 adalah 11111110.

Frxstrem
sumber
3

Dugaan saya adalah bahwa itu ada karena itu beberapa karakter lebih pendek (yang selalu dicari oleh penulis perpustakaan). Ini juga menggunakan operasi yang hanya mengambil beberapa siklus mesin saat dikompilasi ke dalam kode asli (sebagai lawan dari perbandingan dengan angka.)

Saya setuju dengan jawaban lain bahwa ini berlebihan tetapi mungkin masuk akal dalam loop yang ketat (memerlukan estimasi perolehan kinerja, meskipun, jika tidak, mungkin berubah menjadi pengoptimalan prematur.)

Alexander Pavlov
sumber
2

Saya berasumsi, karena ini adalah operasi bitwise, ini adalah cara tercepat (murah secara komputasi) untuk memeriksa apakah jalur muncul di modifiedPaths.

panos2point0
sumber
1

Seperti (~(-1)) === 0, jadi:

!!(~(-1)) === Boolean(~(-1)) === Boolean(0) === false
Insinyur
sumber
1
Ini mungkin akurat, tetapi apakah ini penjelasan yang berguna bagi penanya? Tidak semuanya. Jika saya tidak memahaminya sejak awal, jawaban singkat seperti ini tidak akan membantu.
Spudley
Saya pikir jawaban ini masuk akal. Jika Anda memiliki otak matematika, Anda dapat dengan jelas melihat bagian mana yang berubah di setiap langkah. Apakah ini jawaban terbaik untuk pertanyaan ini? Tidak. Tapi itu berguna, kurasa begitu! +1
Taylor Lopez