Làm thế nào để !! ~ (không phải dấu ngã / dấu ngã bang bang) thay đổi kết quả của một lệnh gọi phương thức Mảng 'chứa / bao gồm'?


95

Nếu bạn đọc các bình luận tại inArraytrang jQuery ở đây , có một khai báo thú vị:

!!~jQuery.inArray(elm, arr) 

Bây giờ, tôi tin rằng dấu chấm than kép sẽ chuyển đổi kết quả thành loại boolean, với giá trị là true. Điều tôi không hiểu là việc sử dụng ~toán tử dấu ngã ( ) trong tất cả những điều này là gì?

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

Cấu trúc lại ifcâu lệnh:

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

Phá vỡ:

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

Tôi cũng nhận thấy rằng nếu tôi đặt dấu ngã ở phía trước, kết quả là -2.

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

Tôi không hiểu mục đích của dấu ngã ở đây. Ai đó có thể vui lòng giải thích nó hoặc chỉ cho tôi về một nguồn tài nguyên?


50
Bất cứ ai viết mã như vậy cần phải rời khỏi bàn phím.
Kirk Woll

12
@KirkWoll: Tại sao? ~jQuery.inArray()thực sự rất hữu ích - thậm chí có thể là một lý do rất tốt tại sao các hàm tìm kiếm trả về -1lỗi (giá trị duy nhất có phần bù của hai là sai). Khi bạn đã xem và hiểu thủ thuật này, tôi cảm thấy nó thậm chí còn dễ đọc hơn != -1.
Amadan

9
@Amadan - không. Không. Nghiêm túc mà nói, tôi không thể tin rằng bạn đang bảo vệ !!~cho bất cứ điều gì .
Kirk Woll

24
Vấn đề là, nó chỉ là: Một "mẹo". Sự khác biệt chính giữa if (x != -1)if (~x)đối với tôi, là cái trước thực sự thể hiện những gì bạn định làm. Phần sau thể hiện rằng bạn muốn làm điều gì đó hoàn toàn khác ("vui lòng chuyển đổi Số 64 bit của tôi thành số nguyên 32 bit và kiểm tra xem bitwise KHÔNG của số nguyên đó có phải là sự thật hay không"), nơi bạn chỉ tình cờ nhận được kết quả mong muốn trong điều này một trường hợp.
JimmiTh

10
>= 0có lẽ là không đủ leet , vì vậy càng khó hiểu hơn !!~được sử dụng.
Yoshi

Câu trả lời:


56

Toán tử dấu ngã thực sự không phải là một phần của jQuery - nó là một toán tử KHÔNG bit trong JavaScript.

Xem Bí ẩn vĩ đại của Tilde (~) .

Bạn nhận được các số lạ trong các thử nghiệm của mình bởi vì bạn đang thực hiện một phép toán logic bitwise trên một số nguyên (mà tôi biết, có thể được lưu trữ dưới dạng phần bù của hai hoặc tương tự như vậy ...)

Phần bù của Two giải thích cách biểu diễn một số trong hệ nhị phân. Tôi nghĩ rằng tôi đã đúng.


3
Đã sửa! (Đã thay đổi nó đến một liên kết đó, kỳ lạ đủ, được viết sau câu trả lời ban đầu của tôi ...)
pglhall

121

Có một lý do cụ thể mà đôi khi bạn sẽ thấy ~được áp dụng trước mặt $.inArray.

Về cơ bản,

~$.inArray("foo", bar)

là một cách ngắn hơn để làm

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

$.inArraytrả về chỉ mục của mục trong mảng nếu tìm thấy đối số đầu tiên và trả về -1 nếu không tìm thấy đối số. Điều này có nghĩa là nếu bạn đang tìm kiếm boolean "giá trị này có trong mảng không?", Bạn không thể thực hiện so sánh boolean, vì -1 là giá trị true và khi $ .inArray trả về 0 (giá trị falsy ), nó có nghĩa là nó thực sự được tìm thấy trong phần tử đầu tiên của mảng.

Áp dụng ~toán tử bitwise nguyên nhân -1trở thành 0và khiến 0 trở thành `-1. Do đó, việc không tìm giá trị trong mảng và áp dụng hàm NOT bitwise sẽ dẫn đến giá trị sai (0) và tất cả các giá trị khác sẽ trả về các số khác 0 và sẽ đại diện cho kết quả trung thực.

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

Và nó sẽ hoạt động như dự định.


2
Tính năng này được hỗ trợ tốt như thế nào trong các trình duyệt (bây giờ là năm 2014?) Hay nó đã được hỗ trợ một cách hoàn hảo?
Thuốc nổ

Tôi sẽ ngạc nhiên nếu các thao tác cơ bản như thế này không hoàn hảo.
pcarvalho

104

!!~exprđánh giá falsekhi nào exprthì -1ngược lại true.
Nó giống như expr != -1, chỉ bị hỏng *


Nó hoạt động vì các phép toán theo bit JavaScript chuyển đổi các toán hạng thành số nguyên có dấu 32 bit ở định dạng bổ sung của hai. Như vậy !!~-1được đánh giá như sau:

   -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

Một giá trị khác -1sẽ có ít nhất một bit được đặt thành 0; đảo ngược nó sẽ tạo ra một giá trị trung thực; áp dụng !toán tử hai lần cho một giá trị true sẽ trả về boolean true.

Khi được sử dụng với .indexOf()và chúng tôi chỉ muốn kiểm tra xem kết quả có -1hay không:

!!~"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đánh giá thành false nên điều nàyghê tởmkhông thể được sử dụng một cách đáng tin cậy để kiểm tra -1.


1
Trong một thư viện ổn định, tôi thấy không có vấn đề gì với việc sử dụng ~foo.indexOf(bar), nó không tiết kiệm đáng kể về ký tự hoặc hiệu suất, nhưng nó là một cách viết tắt tương đối phổ biến theo cùng một cách foo = foo || {}.
zzzzBov

6
Nó không phải là vấn đề ... ít nhất là cho đến khi người khác được yêu cầu tiếp tục với mã của bạn.
Salman A


1
@ahsteele, tôi biết rõ về quy tắc đó, tuy nhiên các toán tử bitwise là một phần của mọi ngôn ngữ lập trình mà tôi có thể nghĩ ra. Tôi cố gắng lập trình theo cách mà người có thể đọc được mã . Tôi không ngừng sử dụng các tính năng của một ngôn ngữ đơn giản chỉ vì người khác không hiểu nó, nếu không thì tôi thậm chí sẽ không thể sử dụng!! .
zzzzBov

Nói đúng ra, >= 0không có hành vi giống như !!~. !== -1gần hơn.
Peter Olson

33

~foo.indexOf(bar)là cách viết tắt phổ biến để biểu diễn foo.contains(bar)containshàm không tồn tại.

Thông thường, việc ép kiểu thành boolean là không cần thiết do khái niệm giá trị "falsy" của JavaScript. Trong trường hợp này, nó được sử dụng để buộc đầu ra của hàm là truehoặc false.


6
+1 Câu trả lời này giải thích "tại sao" tốt hơn câu trả lời được chấp nhận.
nalply

18

jQuery.inArray()trả về -1"không tìm thấy", có phần bổ sung ( ~) là 0. Do đó, ~jQuery.inArray()trả về giá trị giả ( 0) cho "không tìm thấy" và giá trị trung thực (một số nguyên âm) cho "tìm thấy". !!sau đó sẽ chính thức hóa falsy / truthy thành boolean thực false/ true. Vì vậy, !!~jQuery.inArray()sẽ cung cấp truecho "tìm thấy" và false"không tìm thấy".


13

Đối ~với tất cả 4 byte intbằng công thức này-(N+1)

VÌ THẾ

~0   = -(0+1)   // -1
~35  = -(35+1)  // -36 
~-35 = -(-35+1) //34 

3
Điều này không phải lúc nào cũng đúng, vì (ví dụ) ~2147483648 != -(2147483648 + 1).
Frxstrem

10

Các ~nhà điều hành là các nhà điều hành Bitwise bổ sung. Kết quả số nguyên từ inArray()là -1, khi phần tử không được tìm thấy hoặc một số số nguyên không âm. Phần bù theo chiều bit của -1 (được biểu diễn dưới dạng nhị phân là tất cả các bit 1) bằng không. Phần bù theo chiều bit của bất kỳ số nguyên không âm nào luôn khác 0.

Do đó, !!~isẽ là truekhi số nguyên "i" là một số nguyên không âm và falsekhi "i" chính xác là -1.

Lưu ý rằng ~luôn ép toán hạng của nó thành số nguyên; nghĩa là, nó buộc các giá trị dấu phẩy động không phải số nguyên thành số nguyên, cũng như các giá trị không phải số.


10

Dấu nghiêng KHÔNG phải là bitwise - nó đảo ngược từng bit của giá trị. Theo nguyên tắc chung, nếu bạn sử dụng ~trên một số, dấu của nó sẽ bị đảo ngược, sau đó 1 sẽ bị trừ.

Do đó, khi bạn làm như vậy ~0, bạn nhận được -1 (0 ngược lại là -0, trừ 1 là -1).

Về cơ bản, đó là một cách tối ưu hóa công phu, siêu vi mô để nhận một giá trị luôn là Boolean.


8

Bạn nói đúng: Mã này sẽ trả về falsekhi indexOfcuộc gọi trả về -1; ngược lại true.

Như bạn nói, sẽ hợp lý hơn nhiều nếu sử dụng những thứ như

return this.modifiedPaths.indexOf(path) !== -1;

1
Nhưng đó là 3 byte nữa để gửi cho khách hàng! chỉnh sửa: (chỉ đùa thôi bằng cách này, được đăng nhận xét của tôi và nhận ra đó là không rõ ràng (mà là cả hai buồn và ngớ ngẩn))
Wesley Murch

@Wesley: Đúng vậy, nhưng nó chỉ phải được gửi cho mỗi máy khách một lần , giả sử máy khách sẽ lưu vào bộ nhớ cache .js. Phải nói rằng, họ có thể sử dụng >=0thay vì !==-1- không có thêm byte để gửi và vẫn dễ đọc hơn so với phiên bản bit-twiddling.
LukeH

2
Ai đang trolling ai ở đây? ;) Tôi đoán tôi đã cho rằng việc viết mã có thể đọc được tốt hơn là mã được tối ưu hóa trước khó hiểu tạo ra những câu hỏi kiểu này. Chỉ cần rút gọn sau và viết mã có thể đọc được, dễ hiểu ngay bây giờ.
Wesley Murch

2
Cá nhân tôi muốn nói rằng điều đó > -1thậm chí còn dễ đọc hơn, nhưng điều đó có lẽ rất chủ quan.
Yoshi

6

Các ~nhà điều hành là Bitwise NOT điều hành. Điều này có nghĩa là nó nhận một số ở dạng nhị phân và biến tất cả các số 0 thành một và một thành số 0.

Ví dụ, số 0 trong nhị phân là 0000000, trong khi -1 là 11111111. Tương tự như vậy, 1 00000001ở dạng nhị phân, trong khi -2 là 11111110.


3

Tôi đoán rằng nó ở đó vì nó ngắn hơn một vài ký tự (mà các tác giả thư viện luôn theo sau). Nó cũng sử dụng các hoạt động chỉ mất một vài chu kỳ máy khi được biên dịch sang mã gốc (trái ngược với việc so sánh với một số).

Tôi đồng ý với một câu trả lời khác rằng đó là một sự quá mức cần thiết nhưng có lẽ có thể có ý nghĩa trong một vòng lặp chặt chẽ (tuy nhiên, yêu cầu ước tính hiệu suất đạt được, nếu không có thể trở thành tối ưu hóa quá sớm.)


2

Tôi giả sử, vì đây là một thao tác theo bit, nên đây là cách nhanh nhất (rẻ về mặt tính toán) để kiểm tra xem đường dẫn có xuất hiện trong các đường dẫn được sửa đổi hay không.


1

Như (~(-1)) === 0vậy:

!!(~(-1)) === Boolean(~(-1)) === Boolean(0) === false

1
Điều này có thể chính xác, nhưng nó có phải là một lời giải thích hữu ích cho người hỏi? Không có gì. Nếu tôi không hiểu nó từ đầu, một câu trả lời ngắn gọn như thế này sẽ không hữu ích.
Spudley

Tôi nghĩ câu trả lời này có lý. Nếu bạn có một bộ não toán học, bạn có thể thấy rõ phần nào đang thay đổi ở mỗi bước. Nó có phải là câu trả lời tốt nhất cho câu hỏi này? Nhưng nó rất hữu ích, tôi nghĩ vậy! +1
Taylor Lopez
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.