Як !! ~ (не тильда / вибух вибуху тильда) змінює результат виклику методу масиву «містить / включає»?


95

Якщо ви читаєте коментарі в JQuery inArrayсторінці тут , є цікава заява:

!!~jQuery.inArray(elm, arr) 

Тепер я вважаю, що подвійний знак оклику перетворить результат на тип booleanзі значенням true. Що я не розумію, так це те, що в усьому цьому використовується ~оператор tilde ( )?

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

Рефакторинг ifтвердження:

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

Зламатися:

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

Також я помітив, що якщо поставити тильду попереду, результат буде -2.

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

Я не розумію мети тут тильди. Хтось може пояснити це чи вказати мені на ресурс?


50
Той, хто писав би такий код, повинен відійти від клавіатури.
Kirk Woll

12
@KirkWoll: Чому? ~jQuery.inArray()насправді є дуже корисним - можливо, навіть дуже вагомою причиною того, чому функції пошуку повертаються -1до відмови (єдине значення, доповненням двох яких є хибність). Після того, як ви побачили і зрозуміли фокус, я відчуваю, що він навіть читабельніший != -1.
Амадан

9
@Amadan - ні. Просто ні. Серйозно, я не можу повірити, що ти захищаєшся ні!!~ за що .
Kirk Woll,

24
Проблема в тому, що це просто: "фішка". Головна відмінність між if (x != -1)і if (~x)для мене полягає в тому, що перший фактично виражає те, що ви маєте намір зробити. Останній виражає, що ви хочете зробити щось інше цілком ("будь-ласка, перетворіть мій 64-розрядний номер у 32-розрядне ціле число та перевірте, чи побітове НЕ це ціле число є істинним"), де ви просто отримаєте бажаний результат у цьому один випадок.
JimmiTh

10
>= 0ймовірно , НЕ Літ досить, тому більш загадковим !!~був використаний.
Йоші

Відповіді:


56

Оператор тильда насправді взагалі не є частиною jQuery - це побітний оператор NOT у самому JavaScript.

Див . Велику таємницю Тильди (~) .

Ви отримуєте дивні числа у своїх експериментах, оскільки виконуєте побітову логічну операцію над цілим числом (яке, наскільки я знаю, може зберігатися як доповнення двох або щось подібне ...)

Доповнення двох пояснює, як зобразити число у двійковому вигляді. Гадаю, я мав рацію.


3
Виправлено! (Змінив його на інше посилання, яке, як не дивно, було написано після моєї оригінальної відповіді ...)
pglhall

121

Існує конкретна причина, яку ви іноді побачите ~застосованою перед собою $.inArray.

В основному,

~$.inArray("foo", bar)

це коротший спосіб зробити

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

$.inArrayповертає індекс елемента в масиві, якщо перший аргумент знайдений, і повертає -1, якщо його не знайдено. Це означає, що якщо ви шукаєте логічне значення "це значення в масиві?", Ви не можете виконати логічне порівняння, оскільки -1 - це неправдиве значення, і коли $ .inArray повертає 0 (значення хибності ), це означає, що його фактично знайдено в першому елементі масиву.

Застосування ~побітового оператора призводить -1до 0, а 0 - до `-1. Таким чином, не знаходження значення в масиві та застосування побітового NOT призводить до хибного значення (0), а всі інші значення повернуть числа, що не дорівнюють 0, і представлятимуть справжній результат.

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

І це буде працювати за призначенням.


2
Наскільки це добре підтримується у браузерах (тепер у 2014 році?) Або він підтримувався ідеально весь час?
Таблетки від вибуху

я був би здивований, якби такі основні операції не були ідеальними.
pcarvalho

104

!!~exprмає значення , falseколи exprв -1іншому випадку true.
Це те саме expr != -1, що тільки зламаний *


Це працює, оскільки побітові операції JavaScript перетворюють операнди у 32-розрядні цілі числа зі знаком у форматі доповнення двох. Таким чином !!~-1, оцінюється наступним чином:

   -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

Значення, відмінне від того, що -1буде мати принаймні один біт, встановлений на нуль; перевернувши його, ви створите неправдиве значення; застосування !оператора двічі до істинного значення повертає логічне значення true.

При використанні з, .indexOf()і ми хочемо лише перевірити, є результат -1чи ні:

!!~"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оцінює як false, так що цегидотане може бути надійно використаний для тестування на -1.


1
У стабільній бібліотеці я не бачу проблем із використанням ~foo.indexOf(bar), це не суттєва економія символів або продуктивності, але це відносно поширений стенограф, таким же чином foo = foo || {}.
zzzzBov

6
Це не проблема ... принаймні, доки когось іншого не попросять продовжити з вашим кодом.
Салман,

9
@zzzzBov, щоб розширити коментар Салмана : завжди
ahsteele

1
@ahsteele, я добре знаю це правило, проте побітові оператори є частиною кожної мови програмування, яку я можу придумати. Я намагаюся програмувати так, щоб його було зрозуміло тому, хто вміє читати код . Я не перестаю використовувати функції мови просто тому, що хтось інший її не розуміє, інакше я навіть не зміг би користуватися!! .
zzzzBov

Строго кажучи, >= 0не має такої поведінки, як !!~. !== -1ближче.
Пітер Олсон,

33

~foo.indexOf(bar)є типовою стенограмою для представлення, foo.contains(bar)оскількиcontains функція не існує.

Зазвичай приведення в логічну форму є непотрібним через концепцію JavaScript "хибних" значень. У цьому випадку він використовується для примусового виведення функції на trueабо false.


6
+1 Ця відповідь пояснює "чому" краще, ніж прийнята відповідь.
звичайно

18

jQuery.inArray()повертає -1для "не знайдено", чиїм komplement ( ~) є 0. Таким чином, ~jQuery.inArray()повертає хибне значення ( 0) для "не знайдено", а достовірне значення (від'ємне ціле число) для "знайдено". !!потім оформить хибність / неправдиве в справжнє логічне значення false/ true. Отже, !!~jQuery.inArray()дадуть trueза "знайдено" і falseза "не знайдено".


13

Для ~всіх 4 байт intдорівнює цій формулі-(N+1)

ТАК

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

3
Це не завжди правда, оскільки (наприклад) ~2147483648 != -(2147483648 + 1).
Frxstrem

10

~Є оператором побітового доповнення. Результатом цілого числа inArray()є або -1, коли елемент не знайдений, або якесь невід’ємне ціле число. Побітове доповнення -1 (представлене у двійковому вигляді як усі 1 біти) дорівнює нулю. Побітове доповнення будь-якого цілого невід’ємного числа завжди є ненульовим.

Таким чином, !!~iбуде, trueколи ціле число "i" є невід'ємним цілим числом, а falseколи "i" дорівнює -1.

Зверніть увагу, що ~завжди примушує свій операнд до цілого числа; тобто примушує нецілі числа з плаваючою комою до цілих чисел, а також нечислові значення.


10

Тильда побітовим способом НЕ - вона інвертує кожен біт значення. Як загальне емпіричне правило, якщо ви використовуєте ~число, його знак буде інвертованим, тоді 1 буде віднято.

Таким чином, коли ви це зробите ~0, ви отримаєте -1 (0 перевернутий дорівнює -0, відняти 1 дорівнює -1).

По суті, це складний, супермікро-оптимізований спосіб отримання значення, яке завжди булеве.


8

Ви маєте рацію: цей код повернеться, falseколи indexOfдзвінок поверне -1; інакше true.

Як ти кажеш, було б набагато розумніше використовувати щось на зразок

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

1
Але це ще 3 байти, які слід надіслати клієнту! редагувати: (просто жартуючи між іншим, опублікував мій коментар і зрозумів, що це не було очевидно (що і сумно, і безглуздо))
Уеслі Марч

@Wesley: Це правда, але вона повинна бути надіслана кожному клієнту лише один раз , за умови, що клієнт кешує файл .js. Сказавши це, вони могли >=0скоріше скористатися , ніж !==-1- ніякими зайвими байтами для надсилання, і все ще більш читабельними, ніж версія з бітовим обертанням.
LukeH

2
Хто тут кого тролює? ;) Думаю, я сприйняв як даність, що писати читабельний код краще, ніж загадковий заздалегідь оптимізований код, який генерує подібні запитання. Просто мініфікуйте пізніше і напишіть зрозумілий, зрозумілий код зараз.
Wesley Murch

2
Особисто я б сказав, що > -1це навіть більш читабельно, але це, мабуть, дуже суб'єктивно.
Йоші

6

~Оператор побітового НЕ оператор. Це означає, що воно приймає число у двійковій формі і перетворює всі нулі в одиниці, а одиниці - в нулі.

Наприклад, число 0 у двійковій формі дорівнює 0000000, а -1 дорівнює 11111111. Аналогічно, 1 є 00000001в двійковому, а -2 - 11111110.


3

Я здогадуюсь, що він є там, тому що він на кілька символів коротший (за чим автори бібліотек завжди домагаються). Він також використовує операції, які займають лише декілька машинних циклів при компіляції у власний код (на відміну від порівняння з числом).

Я погоджуюсь з іншою відповіддю, що це надмірний рівень, але, можливо, це може мати сенс у вузькому циклі (однак вимагає оцінки приросту продуктивності, інакше може виявитися передчасною оптимізацією).


2

Я припускаю, оскільки це побітова операція, це найшвидший (обчислювально дешевий) спосіб перевірити, чи з'являється шлях у ModifiedPaths.


1

Як (~(-1)) === 0, так:

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

1
Це може бути точно, але чи корисно це пояснення для допитувача? Зовсім не. Якби я не зрозумів цього для початку, така стисла відповідь не допомогла б.
Spudley

Я думаю, що ця відповідь має сенс. Якщо у вас математичний мозок, ви можете чітко бачити, які частини змінюються на кожному кроці. Це найкраща відповідь на це питання? Ні. Але це корисно, я думаю! +1
Тейлор Лопес
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.