Understanding bits and bitwise operators in JavaScript a bit better!

Vat is a bit?
Bit is actually an abbreviation for binary digit. Binary can be defined as relating to, composed of, or involving two things — computers use 0 and 1’s to communicate, convey information and complete actions. It is the computer language. All programming languages are broken down into 0 and 1’s for the computer to understand.
The binary digit 10110101 can be broken down as follows:

Resulting in the computation 128 + 32 + 16 + 4 + 1 and the sum is 181.
Try these on for size if you want a better understanding:
1 --> 00000001
2 --> 00000010
3 --> 00000011
4 --> 00000100
5 --> 00000101
12 --> 00001100
18 --> 00010010
25 --> 00011001
60 --> 00111100
178 --> 10110010
JavaScript
is a programming language and the master of web development. With the introduction and use of JavaScript, the web is now as we know it! While HTML is a markup language and the skeleton of a website and CSS being the clothes and fashion of an individual providing style and flair, JavaScript is communication itself. It allows one to interact with websites — you could go as far as to say it is a website’s behavior.

Negative Numbers
There are three techniques for dealing with negative people, I mean negative numbers — Signed Magnitude, One’s Complement, and Two’s Complement.
Signed Magnitude
is the notion that a binary digit can tell us whether or not it’s positive or negative with its leftmost digit. Take the aforementioned 10110101.
“JavaScript treats their operands as a sequence of 32 bits…” — 10110101 is actually being treated as 00000000000000000000000010110101.
Therefore the leftmost digit is 0, telling us this is a positive number. The opposite is true where 1 refers to negative numbers.
If we take the number 12 and turn it into binary, it would be 00001100. What would -12 be then? It would be 10001100.
Note: too lazy, and unnecessary to make these into 32 bit sequences to drive this point home.
One’s Complement
is the technique where you convert a binary digit to its completely opposite form — 10110101 would then become 01001010. The 0’s change into 1’s and vice-versa.
Two’s Complement
is a technique to negate a number, turn a positive into a negative and turn a negative into a positive.
5 --> 0101 // as 4 bit0101 --> 1010 // take the complement// add one
1010
+ 0001
------
10111011 may look like
8 + 2 + 1 = 11but it's actually
-8 + 2 + 1 = -5 // remember Signed Magnitude from above
By using this technique, you can change a subtraction equation into an addition equation. Just like in math, or maths like our English friends across the pond like to say, you can reflect addition and subtraction equations as follows:
12 - 5
12 + -5
-5 + 12
The result, 7, remains the same and true regardless how you prefer to look at it! Therefore it would look like this for binary digits:
12 - 5
1100 - 101 // binary forms
1100 + 010 // take 5’s binary's complement
The addition from here is as we all know:
1100
+ 010
------
1110
The exception now is that we take the first digit, 1, remove it here and add it:
110
+ 1
-----
111 --> 4 + 2 + 1 --> 7

Bitwise Logical Operators
The operators are &, |, ^, and ~.




& or AND, is much like the more familiar && and returns only if both are the same, or true.
| or OR, as you might’ve guessed, is like || and returns 1 or false whenever it is present.
^ compares and returns true or 0 if both are the same and 1 if they aren’t.
~ or NOT, converts 0 into 1 and 1 into 0.
Bitwise Shift Operators
We have the left shift (<<), right shift (>>), and zero-fill right shift (>>>) operators.
If you’re familiar with Ruby then these may look familiar and are known as the shovel operator — “shovel” may still be a good term to keep in mind as you are moving, shifting or shoveling the order around.
Left
As the name suggests by using <<, you are shifting or moving the order of a binary digit to the left, to the left.

- Converts the digit to its binary form
- Shifts everything to the left x times, and fills the emptiness with 0's
- Converts the new binary digit to its decimal form
Operation Computation Result
15 << 2 => 1111 + 00 = 111100 --> 60
The left shift operator effectively multiplies the digit by 2 as a result.
15 << 0 = 15
15 << 1 = 30
15 << 2 = 60
15 << 3 = 120
15 << 4 = 240
Right
The side where one believes they’re in, but their partner in crime always refutes.

Operation Computation Result
15 >> 2 => 01111 --> 011
00 + 11 = 0011 --> 3
- Converts the digit to its binary form
- Removes x spaces from the right
- Fills the emptiness with the leftmost bit
- Converts the new binary digit to its decimal form
The right shift operator is also known for its sign-propagating. Why? Because you keep the leftmost digit the same, thus the sign (+ / -) is also the same.
Zero-Fill Right
The same as the right shift operator, but instead of filling in empty spaces with the leftmost digit, you fill it in with 0's.
Operation Computation Result
9 >>> 2 1001 --> 0010 2 -9 >>> 2 11111111111111111111111111110111
-->
00111111111111111111111111111101
1073741821
How can any of this actually be helpful?
You could use this to determine if a number is odd or even:
const x = 5.toString(2) // convert a number into binary
x[x.length - 1] === '1' // checks the last digit
// => true AKA it's an odd number
Or more handily, you can use this to better determine combinations of true/false statements using flags and masks.
Some Fun

I wanted to manually do some maths — it’s supposed to make sense since it’s short for mathematics, but i digress — with bits and JavaScript:
function digitToBinary(n) {
return n.toString(2)
}function binaryPad(n, len) {
return n.padStart(len, '0')
}function binarySetup(n1, n2) { let b1 = digitToBinary(n1)
let b2 = digitToBinary(n2)
let len = b1.length > b2.length ? b1.length : b2.length if (b1.length !== len) {
b1 = binaryPad(b1, len)
} if (b2.length !== len) {
b2 = binaryPad(b2, len)
} return [b1, b2]
}function binaryComplement(b) {
return b.split('').map(n => { if (n === '0') {
return '1'
} if (n === '1') {
return '0'
}
}).join('')
}function addBits(b1, b2) { constlen = b1.length
const s1 = b1.split('')
const s2 = b2.split('')
let bitSum = '' for (let i = len - 1; i >= 0; i--) { let sum = parseInt(s1[i], 10) + parseInt(s2[i], 10) if (sum === 0 || sum === 2) {
bitSum = '0' + bitSum
} if (sum === 1 || sum === 3) {
bitSum = '1' + bitSum
} if (sum === 2 || sum === 3) { if (i !== 0) {
s1[i - 1] = parseInt(s1[i - 1], 10) + 1 } else {
bitSum = "1" + bitSum;
}
}
} return bitSum
}
function subtractBits(b1, b2) { b2 = binaryComplement(b2) let subtotal = addBits(b1, b2)
subtotal = subtotal.slice(1)
const negative = binaryPad('1', subtotal.length)
const total = addBits(subtotal, negative) return total
}function addNumbers(n1, n2) { const [b1, b2] = binarySetup(n1, n2)
const binarySum = addBits(b1, b2) return parseInt(binarySum, 2)
}function subtractNumbers(n1, n2) { const [b1, b2] = binarySetup(n1, n2)
const binaryDiff = subtractBits(b1, b2) return parseInt(binaryDiff, 2)
}
Sources
There was another one, but I couldn’t find it again but props — it was very educational!