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

I saw it in an answer, and I've never seen it before.

What does it mean?

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
wwaawaw
  • 5,927
  • 9
  • 24
  • 39
  • 2
    possible duplicate of [What does !!~ (double not and tilde) preceding this array method call do?](http://stackoverflow.com/q/9316612/1048572) – Bergi Jun 02 '15 at 20:42

5 Answers5

284

~ is a bitwise operator that flips all bits in its operand.

For example, if your number was 1, its binary representation of the IEEE 754 float (how JavaScript treats numbers) would be...

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

So ~ converts its operand to a 32 bit integer (bitwise operators in JavaScript do that)...

0000 0000 0000 0000 0000 0000 0000 0001

If it were a negative number, it'd be stored in 2's complement: invert all bits and add 1.

...and then flips all its bits...

1111 1111 1111 1111 1111 1111 1111 1110

So what is the use of it, then? When might one ever use it?

It has a quite a few uses. If you're writing low level stuff, it's handy. If you profiled your application and found a bottleneck, it could be made more performant by using bitwise tricks (as one possible tool in a much bigger bag).

It's also a (generally) unclear trick to turn indexOf()'s found return value into truthy (while making not found as falsy) and people often use it for its side effect of truncating numbers to 32 bits (and dropping its decimal place by doubling it, effectively the same as Math.floor() for positive numbers).

I say unclear because it's not immediately obvious what it is being used for. Generally, you want your code to communicate clearly to other people reading it. While using ~ may look cool, it's generally too clever for its own good. :)

It's also less relevant now that JavaScript has Array.prototype.includes() and String.prototype.includes(). These return a boolean value. If your target platform(s) support it, you should prefer this for testing for the existence of a value in a string or array.

alex
  • 438,662
  • 188
  • 837
  • 957
  • So what is the use of it, then? When might one ever use it? – wwaawaw Sep 06 '12 at 12:04
  • 2
    Is nasty the right word? If it works I'd just call it an idiom of the language. There's many idioms. Once you learn them they are not unclear. List comprehensions are not clear in Python if you don't know them and can be accomplished with more verbose loops but you'd never ask a Python programmer not to use them. Similarly `value = value || default` in JavaScript is a common and valid idiom as long as you know when you can and can't use it. – gman May 16 '14 at 04:37
  • 3
    @gman I guess it doesn't really matter if someone uses it or not. I think comparing list comprehensions (language feature) to this isn't really the same thing (*clever* way to avoid typing some extra characters). If you think *nasty* is too harsh a term, please feel free to edit my answer. – alex May 16 '14 at 04:52
  • 2
    Maybe a more common example is `v = t ? a : b;`. I find that much clearer than `var v; if (t} { v = a; } else { v = b; }` usually broken across 5+ lines and also clearer than `var v = b; if (t) { v = a; }` which would usually be 4+ lines. But I know lots of people not familiar with the `? :` operators who would prefer the second or third way. I find the first is more readable. I agree with the general principle, make the code clear, don't use hacks. I guess I just see `~v.indexOf('...')` to be very clear once I've learned it. – gman May 16 '14 at 15:56
  • 11
    When you're working in a large corporation with many developers you want code to be clearly written (ENGLISH) and well documented. The point of high level coding in a language with garbage collection is to avoid thinking about binary operations, and many front-end developers don't even have assembly language experience under their belts. – user2867288 Dec 12 '16 at 18:55
  • A helpful article explaining the `~` operator and some of it's common uses as described in this answer: https://www.joezimjs.com/javascript/great-mystery-of-the-tilde/ – munsellj Feb 01 '17 at 16:28
  • 1
    Being that it's 5 years later: All major browsers, including Edge, except IE (any version) support `includes()`. Since W3 says more people use IE/Edge than opera and Safari combined, this is something to be wary of if you're developing with **IE** at all in mind. To make matters worse, browser detection in 2017 is still a muddy thing. Every detector I tried got one of Chrome/IE/Edge wrong, one identifying Edge as Chrome. – Regular Jo Nov 03 '17 at 16:26
  • @RegularJoe You should almost never try to do browser detection, it makes many years that everybody understood that doing "feature" detection is way more relevant. Detecting the includes() method is very easy and polyfying it for IE is straightforward and even better, most JS/HTML5 API MDN documentation include a Polyfill section (ex: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes#Polyfill ) – Alexandre Morgaut Nov 29 '17 at 11:21
  • 3
    i wouldn't call `~` idiomatic. it's technically part of the language *spec*, but it's not so much part of the language *in general use*. – worc Aug 02 '18 at 22:37
119

Using it before an indexOf() expression effectively gives you a truthy/falsy result instead of the numeric index that's directly returned.

If the return value is -1, then ~-1 is 0 because -1 is a string of all 1 bits. Any value greater than or equal to zero will give a non-zero result. Thus,

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

will cause the if code to run when "something" is in "someString". If you try to use .indexOf() as a boolean directly, then that won't work because sometimes it returns zero (when "something" is at the beginning of the string).

Of course, this works too:

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

and it's considerably less mysterious.

Sometimes you'll also see this:

var i = ~~something;

Using the ~ operator twice like that is a quick way to convert a string to a 32-bit integer. The first ~ does the conversion, and the second ~ flips the bits back. Of course if the operator is applied to something that's cannot be converted to a number, you get NaN as a result. (edit — actually it's the second ~ that is applied first, but you get the idea.)

Pointy
  • 371,531
  • 55
  • 528
  • 584
  • 2
    For those who don't want to negate bit by bit, `~` when performed on integers is equal to `-(x + 1)`. – Fabrício Matté Sep 06 '12 at 12:08
  • Seems like, well, you know, _**NEGATIVE**_ numeric values should return negative boolean ones as well in the first place. But just another one of JS' fails, I guess? – wwaawaw Sep 06 '12 at 12:14
  • 7
    @adlwalrus well the tradition of `0` being `false` and non-zero being `true` dates way back, at least to C in the '70s and probably lots of other then-contemporary systems programming languages. It probably stems from the way the hardware works; lots of CPUs set a zero bit after an operation, and have a corresponding branch instruction to test it. – Pointy Sep 06 '12 at 12:32
  • 4
    A quicker way to convert it to a 32 bit int would be `| 0`, in which case its only one operation. – alex Dec 18 '15 at 09:22
  • @alex indeed, though we can't necessarily trust the runtime not to interpret a simple application of `~~` exactly the same way. – Pointy Dec 18 '15 at 14:23
  • @wwaawaw There's no such thing as "negative boolean ones", boolean is 1 or 0, except if you mean negative binary values... – Hejazzman Aug 12 '16 at 10:27
33

The ~ is Bitwise NOT Operator, ~x is roughly the same as -(x+1). It is easier to understand, sort of. So:

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

Consider -(x+1). -1 can perform that operation to produce a 0.

In other words, ~ used with a range of number values will produce a falsy (coerce to false from 0) value only for the -1 input value, otherwise, any other truthy value.

As we know, -1 is commonly called a sentinel value. It is used for many functions that return >= 0 values for success and -1 for failure in C language. Which the same rule of return value of indexOf() in JavaScript.

It is common to check of presence/absence of a substring in another string in this way

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
}

However, it would be more easily to do it through ~ as below

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
}

You Don't Know JS: Types & Grammar by Kyle Simpson

Donald Duck
  • 6,488
  • 18
  • 59
  • 79
zangw
  • 33,777
  • 15
  • 127
  • 153
  • 1
    It's _definitely_ easier to understand at face value, even if a person doesn't understand the background that makes it work. I'd take a second look at `-(x+1)` if I saw it in an if statement. The tilde tells me exactly what it's doing to compensate for Javascript's 0-based nature. _Also, the less parentheses the better for reading_ – Regular Jo Nov 29 '17 at 18:00
  • In your initial check code block you could type less by using `if (a.indexOf("Ba") > -1) {// found} //true` which, although a bit lengthier than the tilde examples, is considerably less than the two examples you gave and for new programmers `var opinion = !~-1 ? 'more' : 'less'` understandable. – Hmerman6006 Apr 13 '20 at 11:35
26

~indexOf(item) comes up quite often, and the answers here are great, but maybe some people just need to know how to use it and "skip" the theory:

   if (~list.indexOf(item)) {
     // item in list
   } else {
     // item *not* in list
   }
Jorge Bucaran
  • 5,124
  • 1
  • 26
  • 46
  • 1
    I concur. Airbnb JavaScript Style Guide disallows `++` and `--` because they "encourage excessive trickiness" and yet somehow `~` survived (lurking in the shadows) https://github.com/airbnb/javascript/issues/540 – Shanimal Apr 28 '17 at 00:28
  • @Shanimal The alternative is `list.indexOf(item) >= 0` or `... > -1` since javascript is zero-based and didn't choose to address this from the outset. Further, just opinion (same as Airbnb's), anyone who is doing anything meaningful in javascript knows `++`, and while `--` is less common, the meaning can be inferred. – Regular Jo Nov 29 '17 at 17:52
  • @RegularJoe I agree with most of that. I personally haven't needed `++` and `--` in a while because of primitive methods like `map`, `forEach` etc. My point is more about why they didn't also consider `~` excessively tricky when whatever standard used includes increment and decrement operators. To forbid something so CIS101 doesn't make any sense. – Shanimal Dec 21 '17 at 01:33
12

For those considering using the tilde trick to create a truthy value from an indexOf result, it is more explicit and has less magic to instead use the includes method on String.

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

Note that this is a new standard method as of ES 2015 so it won't work on older browsers. In cases where that matters, consider using the String.prototype.includes polyfill.

This feature is also available for arrays using the same syntax:

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

Here is the Array.prototype.includes polyfill if you need older browser support.

Dana Woodman
  • 3,003
  • 28
  • 28
  • 2
    Avoid using includes(). It is not supported in any version of IE (not just older browsers) at the time of writing: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/includes – Onshop Nov 22 '16 at 12:14
  • 10
    Or just use a compiler, so you can write clearer code, instead of writing the language according to the worst common denominator JS interpreter out there... – Kyle Baker Feb 12 '17 at 19:54
  • 5
    @Ben is right, it doesn't work in Netscape 4.72 either. – mikemaccana Jul 16 '18 at 14:13