Що робить тильда, коли їй передує вираз?


195
var attr = ~'input,textarea'.indexOf( target.tagName.toLowerCase() )
           ? 'value'
           : 'innerHTML'

Я бачив це у відповіді, і ніколи раніше цього не бачив.

Що це означає?


Відповіді:


268

~є побітним оператором, який перевертає всі біти в своєму операнді.

Наприклад, якби ваш номер був 1, його двійкове представлення плавця IEEE 754 (як JavaScript трактує числа) було б ...

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

Тож ~перетворює свій операнд на 32-бітове ціле число (побітові оператори в JavaScript це роблять) ...

0000 0000 0000 0000 0000 0000 0000 0001

Якби це від’ємне число, воно зберігалося б у додатку 2: переверніть усі біти та додайте 1.

... а потім гортає всі свої шматочки ...

1111 1111 1111 1111 1111 1111 1111 1110

То в чому ж це користь? Коли хто-небудь міг би ним користуватися?

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

Це також (зазвичай) неясність трюк , щоб перетворити indexOf()«и знайшов повертається значення в truthy (при прийнятті не знайшли , як falsy ) , і люди часто використовують його для його побічного ефекту усічення чисел до 32 біт (і скинувши його десяткове місце, подвоюючи його, ефективно те саме, що і Math.floor()для додатних чисел).

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

Це також менш актуально зараз, коли у JavaScript є Array.prototype.includes()і String.prototype.includes(). Вони повертають булеве значення. Якщо ваша цільова платформа (-и) підтримують її, вам слід віддати перевагу цьому для тестування на наявність значення в рядку або масиві.


2
Неприємне правильне слово? Якщо це працює, я б просто назвав це ідіомою мови. Ідіом багато. Після того, як ви їх навчитеся, вони не зрозумілі. Зрозуміння списків не зрозуміло в Python, якщо ви їх не знаєте, і їх можна виконати за допомогою багатослівних циклів, але ви ніколи не просите програміста Python не використовувати їх. Точно так само value = value || defaultв JavaScript є поширеною та дійсною фразеологією, якщо ви знаєте, коли ви можете та не можете їх використовувати.
gman

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

2
Можливо, є більш поширений приклад v = t ? a : b;. Я вважаю, що набагато чіткіше, ніж var v; if (t} { v = a; } else { v = b; }зазвичай, розбиті на 5+ рядків, а також ясніше, ніж var v = b; if (t) { v = a; }зазвичай 4+ рядків. Але я знаю багато людей, не знайомих з ? :операторами, які віддають перевагу другому чи третьому способу. Я вважаю, що перша читабельніше. Я погоджуюсь із загальним принципом, уточнюйте код, не використовуйте хаки. Я думаю, що я просто бачу ~v.indexOf('...')бути дуже зрозумілим, як тільки я дізнався це.
gman

9
Працюючи у великій корпорації з багатьма розробниками, ви хочете, щоб код був чітко написаний (АНГЛІЙСЬКИЙ) та добре задокументований. Сенс кодування високого рівня в мові зі збиранням сміття полягає в тому, щоб не думати про бінарні операції, і багато розробників, що працюють на передовій, навіть не мають досвіду мовної збірки.
користувач2867288

3
я б не назвав ~ідіотичним. технічно це частина мовної специфікації , але це не стільки частина мови в цілому .
worc

110

Використання його перед indexOf()виразом ефективно дає результат truthy / false, а не числовий індекс, який безпосередньо повертається.

Якщо значення повернення є -1, то ~-1це 0тому -1, що це рядок з усіх 1 біт. Будь-яке значення, що перевищує нуль або дорівнює, дасть ненульовий результат. Таким чином,

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

призведе ifдо запуску коду, коли "щось" знаходиться в "someString". Якщо ви намагаєтесь використовувати .indexOf()як булевий напрямок, то це не вийде, оскільки іноді він повертає нуль (коли "щось" знаходиться на початку рядка).

Звичайно, це теж працює:

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

і це значно менш загадково.

Іноді ви також побачите це:

var i = ~~something;

Використання ~оператора вдвічі подібного швидкого способу перетворення рядка в 32-бітове ціле число. Перший ~робить перетворення, а другий ~відвертає біти назад. Звичайно, якщо оператор застосовується до чогось, що неможливо перетворити на число, ви отримуєте NaNрезультат. ( редагувати - насправді це друге, ~що застосовується перше, але ви отримуєте ідею.)


2
Для тих, хто не хоче нівелювати побіжно, ~при виконанні на цілих числах дорівнює -(x + 1).
Fabrício Matté

Начебто, ну, ви знаєте, NEGATIVE числові значення повинні також повернути негативні булеві значення в першу чергу. Але я думаю, що ще один з JS не вдається?
wwaawaw

7
@adlwalrus також традиція 0буття falseі ненульового буття trueдатується, принаймні, до 70-х років, і, мабуть, багато інших тодішніх сучасних мов програмування систем. Це, мабуть, випливає з того, як працює апаратне забезпечення; багато процесорів встановлюють нульовий біт після операції і мають відповідну інструкцію гілки для тестування.
Pointy

4
Більш швидким способом перетворити його на 32-бітовий int було б | 0, в цьому випадку це лише одна операція.
alex

@alex дійсно, хоча ми не можемо обов'язково довіряти виконанню часу, щоб не інтерпретувати просте застосування ~~точно так само.
Pointy

30

~Є побітовое НЕ Operator , ~xприблизно такий же , як -(x+1). Це легше зрозуміти, начебто. Так:

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

Розглянемо -(x+1). -1може виконати цю операцію, щоб створити 0.

Іншими словами, ~використання з діапазоном значень чисел призведе до помилкового (примусового до falseвід 0) значення лише для -1вхідного значення, в іншому випадку будь-яке інше значення "truthy".

Як ми знаємо, -1прийнято називати вартовим значенням . Він використовується для багатьох функцій, які повертають >= 0значення для успіху та -1для відмови у мові C. Яке саме правило зворотного значення indexOf()в JavaScript.

Зазвичай, таким чином перевіряти наявність / відсутність підрядки в іншому рядку

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
}

Однак зробити це буде легше, ~як показано нижче

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
}

Ви не знаєте JS: Типи та граматика Кайла Сімпсона


1
Це, безумовно, простіше зрозуміти за номіналом, навіть якщо людина не розуміє передумови, які змушують його працювати. Я б по-другому подивився, -(x+1)якби бачив це у заяві if. Тильда точно підказує мені, що вона робить для компенсації природи Javascript, що базується на 0. Крім того, чим менше дужок, тим краще для читання
регулярний Джо

У своєму початковому блоці контрольного коду ви можете набрати менше, використовуючи if (a.indexOf("Ba") > -1) {// found} //trueякий, хоча і трохи довший, ніж приклади тильди, значно менший, ніж два приклади, які ви подали, і для нових програмістів var opinion = !~-1 ? 'more' : 'less'зрозумілі.
Півмісяця

25

~indexOf(item) приходить досить часто, і відповіді тут чудові, але, можливо, деяким людям просто потрібно знати, як ним користуватися і "пропустити" теорію:

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

1
Я погоджуюсь. Посібник зі стилів Airbnb JavaScript відключає, ++і --тому, що вони "заохочують зайву хитрість", і все-таки якось ~вижили (ховаються в тіні) github.com/airbnb/javascript/isissue/540
Shanimal

@Shanimal Альтернатива є list.indexOf(item) >= 0або ... > -1оскільки javascript заснований на нулі і не вирішив вирішувати це з самого початку. Далі, лише думка (така ж, як у Airbnb), кожен, хто робить що-небудь значне в JavaScript, знає ++, і хоча --це є менш поширеним, про це можна визначити значення.
Регулярний Джо

@RegularJoe Я згоден з більшою частиною цього. Мені особисто не потрібні були ++і --через деякий час через примітивні методи, такі як mapі forEachт. Д. Моя думка більше про те, чому вони також не вважали ~надмірно складними, коли будь-який стандарт, що використовується, включає оператори збільшення та зменшення. Забороняти щось, так що CIS101 не має сенсу.
Шанімаль

12

Для тих, хто розглядає можливість використання фокусу tilde, щоб створити значення " truthy" з indexOfрезультату, воно більш чітке і має менше магії замість цього використовувати includesметодString .

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

Зауважте, що це новий стандартний метод на ES 2015, тому він не працюватиме на старих браузерах. У випадках, коли це має значення, розгляньте можливість використання String.prototype.include polyfill .

Ця функція також доступна для масивів, що використовують той самий синтаксис :

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

Ось Array.prototype.includes polyfill, якщо вам потрібна підтримка старшого браузера.


2
Уникайте використання включає (). Він не підтримується в будь-якій версії IE (не тільки у старих браузерах) під час написання: developer.mozilla.org/uk/docs/Web/JavaScript/Reference/…
Onshop

8
Або просто скористайтеся компілятором, щоб ви могли написати чіткіший код, а не писати мову відповідно до найгіршого загального знаменника JS-перекладача ...
Кайл Бейкер,

2
@Ben правий, він також не працює в Netscape 4.72.
mikemaccana
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.