Bitwise Truthiness

In this blog post, I explore another form of truthiness in Javascript. What happens if you use a bitwise operator on a value like 0|value or ~~value.

Context

We recently turned on the JSHint bitwise rule by default and the following code was caught.

var isValid = false;
for (var i = 0; i < elements.length; ++i) {
  isValid |= elements.someProperty;
}
return isValid;

The author didn’t mean to use a bitwise operator and wanted to write the following instead:

  isValid = isValid || elements.someProperty;

Unfortunately, while the two lines look similar, they do not behave the same for all Javascript values. Instead of using the truthiness rules, the value is first converted to a signed 32bit integer and compared against 0.

.bitwise-different { background-color: #ffffe0; }

Booleans and special values

In this system, booleans and special values are behaving as expected:

  • 0 | undefined === 0
  • 0 | null === 0
  • 0 | false === 0
  • 0 | true === 1

Numbers

It becomes a bit more tricky with numbers. The double is first converted to signed 32 bits integer and then compared to 0.

For usual integers, it is working as expected, only 0 is falsy.

  • 0 | 0 === 0
  • 0 | 1 === 1
  • 0 | 42 === 42
  • 0 | -1 === -1

NaN (not a number) is considered as falsy, still going as expected.

  • 0 | NaN === 0

For non integers, it is more tricky. Any number in the range ]-1, 1[ is going to be falsy. Lines highlighted in yellow do not behave the same as normal truthy values.

  • 0 | 0.99 === 0
  • 0 | -0.99 === 0
  • 0 | 1.01 === 1

For big integers, the situation is even more confusing. They are all truthy except multiples of 232. This is true even after 253.

  • 0 | Math.pow(2, 32) === 0
  • 0 | 3 * Math.pow(2, 32) === 0
  • 0 | Math.pow(2, 53) === 0
  • 0 | Math.pow(2, 60) === 0
  • 0 | Math.pow(2, 32) + 1 === 1

Strings

Contrary to normal truthiness, strings in general are falsy.

  • 0 | “” === 0
  • 0 | ” ” === 0
  • 0 | “a” === 0

But, strings that represent numbers now follow number rules.

  • 0 | “0” === 0
  • 0 | “1” === 1
  • 0 | “0.5” === 0

The number parser is not rigid and accept inputs that have whitespace around for example.

  • 0 | ”    1    ” === 1

Objects

Objects are first converted to string before being converted to int32. So nearly all objects are falsy because their string representation is "[object Object]".

  • 0 | {} === 0
  • 0 | {key: 1} === 0

You can still craft objects that passes that check by overriding toString.

  • 0 | {toString: function() { return “1”; }} === 1

Conclusion

The normal truthiness rules can be quite misleading, but if you want to shoot yourself in the foot, I highly encourage you to replace all your !!expression into 0|expression. This will give you endless hours of debugging fun.

If you liked this article, you might be interested in my X feed as well.