Dấu ngã làm gì khi nó đứng trước một biểu thức?


Câu trả lời:


266

~là một toán tử bitwise lật tất cả các bit trong toán hạng của nó.

Ví dụ: nếu số của bạn là 1, biểu diễn nhị phân của số float của IEEE 754 (cách JavaScript xử lý số) sẽ là ...

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

Vì vậy, ~chuyển đổi toán hạng của nó thành số nguyên 32 bit (toán tử bitwise trong JavaScript làm điều đó) ...

0000 0000 0000 0000 0000 0000 0000 0001

Nếu đó là một số âm, nó sẽ được lưu trong phần bù 2: đảo ngược tất cả các bit và thêm 1.

... và sau đó lật tất cả các bit của nó ...

1111 1111 1111 1111 1111 1111 1111 1110

Vì vậy, việc sử dụng nó là gì? Khi nào người ta có thể sử dụng nó?

Nó có khá nhiều công dụng. Nếu bạn đang viết những thứ cấp thấp, nó rất tiện dụng. Nếu bạn lập hồ sơ ứng dụng của bạn và tìm thấy một nút cổ chai, nó có thể được thực hiện nhiều hơn bằng cách sử dụng các thủ thuật bitwise (như một công cụ có thể trong một túi lớn hơn nhiều).

Nó cũng là một (nói chung) không rõ ràng lừa để biến indexOf()'s tìm thấy giá trị trả về vào truthy (trong khi thực hiện không tìm thấy như falsy ) và người ta thường sử dụng nó cho tác dụng phụ của nó cắt xén số đến 32 bit (và thả thập phân của nó bằng cách tăng gấp đôi nó, có hiệu quả tương tự như Math.floor()đối với các số dương).

Tôi nói không rõ ràng vì nó không rõ ràng ngay lập tức nó được sử dụng để làm gì. Nói chung, bạn muốn mã của bạn giao tiếp rõ ràng với những người khác đang đọc nó. Mặc dù sử dụng ~có thể trông rất tuyệt , nhưng nhìn chung nó quá thông minh. :)

Bây giờ nó cũng ít liên quan hơn khi JavaScript có Array.prototype.includes()String.prototype.includes(). Chúng trả về một giá trị boolean. Nếu (các) nền tảng đích của bạn hỗ trợ nó, bạn nên thích điều này để kiểm tra sự tồn tại của một giá trị trong chuỗi hoặc mảng.


2
Là khó chịu từ đúng? Nếu nó hoạt động tôi chỉ gọi nó là một thành ngữ của ngôn ngữ. Có nhiều thành ngữ. Một khi bạn tìm hiểu chúng, chúng không rõ ràng. Việc hiểu danh sách không rõ ràng trong Python nếu bạn không biết chúng và có thể được thực hiện bằng các vòng lặp dài hơn nhưng bạn sẽ không bao giờ yêu cầu lập trình viên Python không sử dụng chúng. Tương tự value = value || defaulttrong JavaScript là một thành ngữ phổ biến và hợp lệ miễn là bạn biết khi nào bạn có thể và không thể sử dụng nó.
gman

3
@gman Tôi đoán nó không thực sự quan trọng nếu ai đó sử dụng nó hay không. Tôi nghĩ rằng việc so sánh mức độ hiểu danh sách (tính năng ngôn ngữ) với điều này thực sự không giống nhau ( cách thông minh để tránh nhập thêm một số ký tự). Nếu bạn nghĩ khó chịu là một thuật ngữ quá khắc nghiệt, xin vui lòng chỉnh sửa câu trả lời của tôi.
alex

2
Có lẽ một ví dụ phổ biến hơn là v = t ? a : b;. Tôi thấy rằng rõ ràng hơn nhiều so với var v; if (t} { v = a; } else { v = b; }thường được chia trên 5+ dòng và cũng rõ ràng hơn var v = b; if (t) { v = a; }thường là 4+ dòng. Nhưng tôi biết nhiều người không quen thuộc với các ? :nhà khai thác, những người thích cách thứ hai hoặc thứ ba. Tôi tìm thấy đầu tiên là dễ đọc hơn. Tôi đồng ý với nguyên tắc chung, làm cho mã rõ ràng, không sử dụng hack. Tôi đoán tôi chỉ thấy ~v.indexOf('...')rất rõ ràng một khi tôi đã học nó.
gman

9
Khi bạn làm việc trong một tập đoàn lớn có nhiều nhà phát triển, bạn muốn mã được viết rõ ràng (TIẾNG VIỆT) và được ghi chép đầy đủ. Quan điểm của mã hóa cấp cao trong một ngôn ngữ với bộ sưu tập rác là để tránh suy nghĩ về các hoạt động nhị phân và nhiều nhà phát triển front-end thậm chí không có kinh nghiệm ngôn ngữ lắp ráp trong vành đai của họ.
dùng2867288

3
tôi sẽ không gọi ~thành ngữ. đó là kỹ thuật phần của ngôn ngữ đặc tả , nhưng nó không phải là quá nhiều phần của ngôn ngữ được sử dụng chung .
worc

108

Sử dụng nó trước một indexOf()biểu thức có hiệu quả mang lại cho bạn kết quả trung thực / sai lệch thay vì chỉ số số được trả về trực tiếp.

Nếu giá trị trả về là -1, sau đó ~-10bởi vì -1là một chuỗi của tất cả 1 bit. Bất kỳ giá trị nào lớn hơn hoặc bằng 0 sẽ cho kết quả khác không. Như vậy

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

sẽ khiến ifmã chạy khi "cái gì đó" nằm trong "someString". Nếu bạn cố gắng sử dụng .indexOf()trực tiếp như một boolean, thì điều đó sẽ không hoạt động vì đôi khi nó trả về 0 (khi "một cái gì đó" ở đầu chuỗi).

Tất nhiên, điều này cũng hoạt động:

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

và nó ít bí ẩn hơn đáng kể.

Đôi khi bạn cũng sẽ thấy điều này:

var i = ~~something;

Sử dụng ~toán tử hai lần như vậy là một cách nhanh chóng để chuyển đổi một chuỗi thành số nguyên 32 bit. Cái đầu tiên ~thực hiện chuyển đổi, và cái thứ hai ~lật lại các bit. Tất nhiên, nếu toán tử được áp dụng cho thứ gì đó không thể chuyển đổi thành số, NaNkết quả là bạn sẽ nhận được . ( chỉnh sửa - thực sự đó là lần thứ hai ~được áp dụng đầu tiên, nhưng bạn có ý tưởng.)


2
Đối với những người không muốn phủ định từng chút một, ~khi được thực hiện trên số nguyên là bằng -(x + 1).
Fabrício Matté

Có vẻ như, tốt, bạn biết đấy, các giá trị số NEGECT sẽ trả về các giá trị boolean âm ngay từ đầu. Nhưng tôi đoán là một lỗi khác của JS 'không thành công?
wwaawaw

7
@adlwalrus cũng là truyền thống 0tồn tại falsevà khác không truengày trở lại, ít nhất là với C trong thập niên 70 và có lẽ rất nhiều ngôn ngữ lập trình hệ thống đương đại khác. Nó có thể bắt nguồn từ cách thức hoạt động của phần cứng; rất nhiều CPU thiết lập một bit 0 sau một hoạt động và có một lệnh rẽ nhánh tương ứng để kiểm tra nó.
Mũi nhọn

4
Một cách nhanh hơn để chuyển đổi nó thành int 32 bit | 0, trong trường hợp đó chỉ là một thao tác.
alex

@alex thực sự, mặc dù chúng ta không nhất thiết có thể tin tưởng thời gian chạy không diễn giải một ứng dụng đơn giản ~~theo cùng một cách.
Pointy

28

Các ~Bitwise NOT điều hành , ~xlà gần giống như -(x+1). Nó là dễ hiểu hơn, loại. Vì thế:

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

Hãy xem xét -(x+1). -1có thể thực hiện thao tác đó để sản xuất a 0.

Nói cách khác, ~được sử dụng với một phạm vi các giá trị số sẽ chỉ tạo ra giá trị giả (ép buộc falsetừ 0) cho -1giá trị đầu vào, nếu không, bất kỳ giá trị trung thực nào khác.

Như chúng ta biết, -1thường được gọi là giá trị sentinel . Nó được sử dụng cho nhiều chức năng trả lại >= 0giá trị cho sự thành công-1cho thất bại trong ngôn ngữ C. Mà cùng một quy tắc về giá trị trả về của indexOf()JavaScript.

Thông thường kiểm tra sự hiện diện / vắng mặt của một chuỗi con trong một chuỗi khác theo cách này

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
}

Tuy nhiên, sẽ dễ dàng hơn để làm điều đó ~như dưới đây

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
}

Bạn không biết JS: Các loại & ngữ pháp của Kyle Simpson


1
nó là chắc chắn dễ dàng hơn để hiểu tại mệnh giá, thậm chí nếu một người không hiểu được nền mà làm cho nó làm việc. Tôi sẽ xem xét lại -(x+1)nếu tôi thấy nó trong một câu lệnh if. Dấu ngã cho tôi biết chính xác những gì nó đang làm để bù đắp cho bản chất dựa trên 0 của Javascript. Ngoài ra, càng ít dấu ngoặc đơn càng tốt cho việc đọc
Joe thường xuyên

Trong khối mã kiểm tra ban đầu của bạn, bạn có thể nhập ít hơn bằng cách sử dụng if (a.indexOf("Ba") > -1) {// found} //true, mặc dù dài hơn một chút so với các ví dụ dấu ngã, ít hơn đáng kể so với hai ví dụ bạn đã đưa ra và cho các lập trình viên mới có thể var opinion = !~-1 ? 'more' : 'less'hiểu được.
HalfMens

24

~indexOf(item) xuất hiện khá thường xuyên và câu trả lời ở đây rất hay, nhưng có lẽ một số người chỉ cần biết cách sử dụng và "bỏ qua" lý thuyết:

   if (~list.indexOf(item)) {
     // item in list
   } else {
     // item *not* in list
   }

1
Tôi đồng tình. Hướng dẫn về Phong cách JavaScript của Airbnb không tuân theo ++--vì họ "khuyến khích sự ~
khôn khéo

@Shanimal Cách thay thế là list.indexOf(item) >= 0hoặc ... > -1vì javascript dựa trên zero và không chọn giải quyết vấn đề này ngay từ đầu. Hơn nữa, chỉ cần ý kiến ​​(giống như Airbnb), bất kỳ ai đang làm bất cứ điều gì có ý nghĩa trong javascript đều biết ++, và trong khi --ít phổ biến hơn, ý nghĩa có thể được suy ra.
Joe thường xuyên

@RegularJoe Tôi đồng ý với hầu hết điều đó. Cá nhân tôi không cần thiết ++--trong một thời gian vì các phương pháp nguyên thủy như map, forEachv.v. Quan điểm của tôi là về lý do tại sao họ cũng không cân nhắc ~quá mức khi bất kỳ tiêu chuẩn nào được sử dụng bao gồm các toán tử tăng và giảm. Để cấm một cái gì đó để CIS101 không có ý nghĩa gì.
Tối

11

Đối với những người xem xét sử dụng các trick dấu ngã để tạo ra một truthy giá trị từ một indexOfkết quả, nó là rõ ràng hơn và có ít phép thuật để thay vì sử dụng các includesphương pháp trênString .

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

Lưu ý rằng đây là một phương pháp tiêu chuẩn mới kể từ ES 2015 vì vậy nó sẽ không hoạt động trên các trình duyệt cũ hơn. Trong trường hợp có vấn đề, hãy cân nhắc sử dụng String.prototype.includes polyfill .

Tính năng này cũng có sẵn cho các mảng sử dụng cú pháp tương tự :

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

Dưới đây là Array.prototype.inc loại polyfill nếu bạn cần hỗ trợ trình duyệt cũ hơn.


2
Tránh sử dụng bao gồm (). Nó không được hỗ trợ trong bất kỳ phiên bản IE nào (không chỉ các trình duyệt cũ hơn) tại thời điểm viết bài: developer.mozilla.org/en/docs/Web/JavaScript/Reference/
trộm

8
Hoặc chỉ cần sử dụng trình biên dịch, do đó bạn có thể viết mã rõ ràng hơn, thay vì viết ngôn ngữ theo trình thông dịch mẫu số chung xấu nhất hiện có ...
Kyle Baker

2
@Ben đúng, nó cũng không hoạt động trong Netscape 4.72.
mikemaccana
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.