Что делает тильда, когда она предшествует выражению?

Ответы:

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 это распространенная и правильная идиома, если вы знаете, когда вы можете и не можете ее использовать.
мужчина
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('...')чтобы быть очень ясным, как только я изучил это.
Ган
9
Когда вы работаете в большой корпорации со многими разработчиками, вы хотите, чтобы код был четко написан (АНГЛИЙСКИЙ) и хорошо документирован. Смысл высокоуровневого кодирования в языке с сборкой мусора заключается в том, чтобы не думать о бинарных операциях, и многие разработчики интерфейса даже не имеют опыта работы с ассемблером.
user2867288
3
я бы не назвал ~идиоматическим. технически это часть языковой спецификации , но это не такая большая часть языка в общем использовании .
worc
110

Использование его перед indexOf()выражением дает эффективный / ложный результат вместо числового индекса, который возвращается напрямую.

Если возвращаемое значение -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).
Фабрицио Мате
Похоже, ну, вы знаете, отрицательные числовые значения должны возвращать отрицательные логические значения, в первую очередь. Но просто еще один из JS не работает, я полагаю?
wwaawaw
7
@adlwalrus хорошо традиция 0бытия falseи ненулевого бытия trueвосходит к далёкому прошлому , по крайней мере, к C в 70-х и, вероятно, ко многим другим тогдашним современным языкам системного программирования. Вероятно, это связано с тем, как работает оборудование; Многие процессоры устанавливают нулевой бит после операции и имеют соответствующую инструкцию перехода для ее проверки.
Заостренный
4
Более быстрый способ преобразовать его в 32-битное int - | 0в этом случае это всего лишь одна операция.
Алекс
@alex действительно, хотя мы не можем доверять среде исполнения, чтобы не интерпретировать простое приложение ~~точно таким же образом.
Заостренный
30

~Является побитовое НЕ Operator , ~xпримерно такой же , как -(x+1). Это легче понять, вроде. Так:

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

Посмотрим -(x+1). -1может выполнить эту операцию, чтобы произвести 0.

Другими словами, ~использование с диапазоном числовых значений приведет к ложному (coerce to falsefrom 0) значению только для -1входного значения, в противном случае - любого другого истинного значения.

Как известно, -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: Типы и грамматика Кайла Симпсона

zangw
источник
1
Это определенно легче понять по достоинству, даже если человек не понимает, что заставляет его работать. Я бы посмотрел второй раз, -(x+1)если бы увидел это в операторе if. Тильда говорит мне, что именно она делает, чтобы компенсировать ноль Javascript. Кроме того, чем меньше скобок, тем лучше для чтения
Обычный Джо
В вашем начальном блоке контрольного кода вы могли бы печатать меньше, используя if (a.indexOf("Ba") > -1) {// found} //trueкоторый, хотя и немного длиннее, чем примеры тильды, значительно меньше, чем два приведенных вами примера, и для новых программистов var opinion = !~-1 ? 'more' : 'less'понятен.
HalfMens
25

~indexOf(item) возникает довольно часто, и ответы здесь великолепны, но, возможно, некоторым людям просто нужно знать, как его использовать и «пропустить» теорию:

   if (~list.indexOf(item)) {
     // item in list
   } else {
     // item *not* in list
   }
Хорхе Букаран
источник
1
Я согласен. Airbnb JavaScript Style Guide запрещает ++и --потому, что они «поощряют чрезмерную хитрость» и все же каким-то образом ~выжили (прячась в тени) github.com/airbnb/javascript/issues/540
Shanimal
@Shanimal Альтернативой является list.indexOf(item) >= 0или, ... > -1поскольку javascript основан на нуле и не решал обратиться к этому с самого начала. Кроме того, просто мнение (то же, что и у Airbnb), любой, кто делает что-то значимое в javascript, знает ++, и хотя это --встречается реже, значение может быть выведено.
Регулярный Джо
@RegularJoe Я согласен с большинством из этого. Лично мне это не понадобилось, ++и --через какое-то время из-за примитивных методов, таких как mapи forEachт. Д. Я хочу больше сказать о том, почему они также не считают ~слишком сложным, когда какой-либо используемый стандарт включает в себя операторы увеличения и уменьшения. Запрещать что-либо, чтобы CIS101 не имело никакого смысла.
Shanimal
12

Для тех, кто рассматривает возможность использования трюка тильды для создания истинного значения из indexOfрезультата, он более явный и имеет меньшую магию вместо использования includesметодаString .

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

Обратите внимание, что это новый стандартный метод с ES 2015, поэтому он не будет работать в старых браузерах. В тех случаях, когда это имеет значение, рассмотрите возможность использования полизаполнения String.prototype.include .

Эта функция также доступна для массивов, использующих тот же синтаксис :

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

Вот полифайл Array.prototype.include, если вам нужна поддержка старых браузеров.

Дана Вудман
источник
2
Избегайте использования include (). Он не поддерживается ни в одной версии IE (не только в старых браузерах) на момент написания статьи: developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Onshop
8
Или просто используйте компилятор, чтобы вы могли писать более понятный код вместо того, чтобы писать язык в соответствии с наихудшим общим знаменателем JS-интерпретатора ...
Кайл Бейкер,
2
@Ben прав, в Netscape 4.72 он тоже не работает.
mikemaccana