From 0 to 9: The Magic of Numbers in JavaScript
Numbers have a hidden power. Sometimes they are used in powerful forms of sorcery—just like in JavaScript. And as long you use them the right way, numbers can be a source of lot of fun in learning JS.
🤓 Note: I'm going to use ES2015 in the following examples. Not all will work
natively everywhere (you need a ES2015 supported interpreter, but don't worry probably you don't have to take care of that; you probably have it already).
Some of the examples are ES2015 specific. In that case, there is a specific note.
So, let's go!
Learning JavaScript by the Numbers—from 0 to 9
0. Zero-based things
Like in other programming languages, array indexes are zero-based. You start counting with zero; why is this so?
First: Whoops, I'll try again.
Zero: It's about saving space. The elements from any array are stored somewhere in the memory.
Imagine the memory looks like this:
Memory: _ _ _ _ _ _ _ _ _ _ _ _ _
Data: [A, B, C]
^ First index
Now, let's assume the array is located in memory at location X. In zero-based indexing, to access the element at index i
(by using array[i]
), the computer has to: get me the content at location X + i
(e.g. for i = 0
, array[X + 0]
will be the first element).
In case we want one-based indexing, we lose one position in the memory buffer:
Memory: _ _ _ _ _ _ _ _ _ _ _ _ _
Data: [ A, B, C]
^ First index
^ Unused memory gap
So the location X is now not used at all, while the next ones are used. If you want to use it, then you need to do a subtraction (get me content at X + i - 1
), which is explained in the next point.
First: It's about optimizing things. Starting the count from zero is more efficient.
Let's take an example:
let fruits = ["Apples", "Pears", "Oranges"];
// Indexes: 0 1 2
The length of the array is 3
(there are three elements). We can say that any index number is matching in the following math expression:
0 ≤ index < 3
0 ≤ 0 < 3 → true
0 ≤ 1 < 3 → true
0 ≤ 2 < 3 → true
Now let's see what would happen if we have a one-based indexing:
let fruits = ["Apples", "Pears", "Oranges"];
// Indexes: 1 2 3
let N = fruits.length;
1 ≤ index < N + 1
1 ≤ 1 < 4
1 ≤ 2 < 4
1 ≤ 3 < 4
You may think now: well, what's the issue here? The problem is when we do N + 1
. It's a bit of unnecessary extra work for the computer.
Second: 1
is the smallest integer which is still a valid value for a non-empty array length.
🤓 Note: The months in JavaScript dates are also zero-based (0-11). If you want to get the first of January 2016, you want to do:
console.log(new Date(2016, 0, 1).toString())
→ Fri Jan 01 2016 00:00:00 GMT+0200 (EET)
Could that be because months have labels attached with the indexes, so it's faster to access them internally (like I mentioned above)? Maybe!
1. Representing numbers and working with them
There are many ways to represent numbers in JavaScript. Here are a few of them. In some cases, the syntax looks weird and even though it's a valid syntax, you probably won't use it. But it's good to know that such cases are possible.
Integers
> 42
42
// The dot is displayed only if there are non-zero digits after the dot
> 42.000
42
Floating point
> 3.14
3.14
Octals (aka base 8)
Octals use a leading 0
:
> 0123
83
Note the above format is forbidden in ES5 strict mode. In ES6, we can use 0o...
:
> 0o123
83
Hex (aka base 16)
> 0xFF
255
> parseInt("0xFF", 16)
255
Exponent
eX
is an abbreviation for multiply with 10ˣ.
> 7e3
7000
Explanation:
7 * 10 * 10 * 10
\__________/
7 * (1)e3
7 * 1000
7000
💡 Tip: It works nicely with negative exponents:
> 7e-2
0.07
Binary (added in ES2015+)
It starts with 0b
, followed by the binary snippet (1
and 0
digits).
> 0b1101
13
Playing with the dot:
> 42.
42
For values lower than 0, you can start directly with the dot:
> .42
0.42
💡 Tip: This works in CSS as well:
div.myElement {
opacity: .5;
}
Number
constructor
2. Playing with the All the numbers above have the Number
constructor:
> a = 42
42
> a.constructor
[Function: Number]
> typeof a
"number"
So, obviously they are numbers. But one thing we should remember is that in JavaScript, everything is an object. So, are numbers objects in JavaScript? Of course!
A simple proof is by adding a new method to the Number
prototype:
Number.prototype.me = function () {
return this;
};
// Let's create a variable
let foo = 42;
// Obviously, it's a number
typeof foo;
// → "number"
// But let's use the `me` method which we added
// (it returns the Number instance)
typeof foo.me();
// → "object"
// And of course it can be used in operations
foo.me() + 1;
// → 43
We can create such numbers using new Number(...)
.
let a = new Number(42);
typeof a;
// → "object"
a + 1;
// → 43
// Note, by outputting this, we get an object:
console.log(a);
// → [Number: 42]
// Calling the valueOf will return the real number
console.log(a.valueOf());
// → 42
Is this actually useful for anything? Well, yes!
The Number
constructor can be used without new
as well. It will convert the things into numbers (read more about this here):
let str = "\n\n42\t\n";
let foo = Number(str);
console.log(foo);
// → 42
Putting these two things together, we can create our own object, and return set a custom conversion when we use it in Number operations:
let magicObject = {
foo: 42
, hello: "world"
// This will return the numeric value
, valueOf () {
return this.foo;
}
};
// Let's convert it into a number
console.log(+magicObject);
// → 43
console.log(-magicObject);
// → -43
// Add 1 to the sum
console.log(magicObject + 1);
// → 43
// Additionally, you can set a custom stringify function:
magicObject.toString = function () { return this.hello; };
console.log(`Hello ${magicObject}!`);
// → "Hello world!"
3. Invoking methods on number literals
The following syntax is strange, but still valid:
> .42.toString()
"0.42"
> 0.42.toString()
"0.42"
💡 Tip: It's a good practice to use wrapping parentheses around such numbers, and here's why:
> (0.42).toString()
"0.42"
> (.42).toString()
"0.42"
> .42.toString()
"0.42"
> 0.42.toString()
"0.42"
// Note this: we do call the toString() function,
// but get a number in the end
> -0.42.toString()
-0.42
// When adding the wrapping parentheses, it's working fine
> (-0.42).toString()
'-0.42'
// But if we take the operator outside of the parentheses,
// we do get a number
> -(0.42).toString()
-0.42
If you use an arithmetic operator there (generally -
), you need to wrap it, too.
Otherwise, you'll get a number in the end. This is because of the operations order:
-0.42.toString()
// \_____________/
// - '0.42'
// -0.42
When you wrap your numbers between parentheses, you simply convert the (negative) number into a string.
===
!== ==
(AKA strict equal)
4. Strictly use strict equal! As mentioned in this article, it's definitely better to use strict equal.
If you know a bit of JavaScript, this is one of the
first things you learn. In most popular programming languages, to check if something equals something else, you're going to have to use ==
:
// Assign the value
let foo = 42;
// Check if `foo` is 42
foo === 42
// → true
In JavaScript, this works but it does something else. It's a liberal equal, if you like to call it so:
"42" == 42
// → true
// Add some spaces, new lines and tabs in that string
"\n\t 42 \t\n\n" == 42
// → true
Using ===
(strict equal) definitely makes a change:
"42" === 42
// → false
5. Special numbers
There are quite a few special numbers:
-0
—negative zero (usually we use the positive value:+0
or just0
)NaN
(not a number)Infinity
and-Infinity
In the Number
object you will find quite a few other constants:
// The largest representable number
> Number.MAX_VALUE
1.7976931348623157e+308
// The smallest representable number
> Number.MIN_VALUE
5e-324
// Special "not a number" value
> Number.NaN
NaN
// Special negative infinite value; returned on overflow
> Number.NEGATIVE_INFINITY
-Infinity
// Special positive infinite value; returned on overflow
> Number.POSITIVE_INFINITY
Infinity
// Difference between one and the smallest value greater
// than one that can be represented as a Number.
> Number.EPSILON
2.220446049250313e-16
// Minimum safe integer in JavaScript.
> Number.MIN_SAFE_INTEGER
-9007199254740991
// Maximum safe integer in JavaScript.
> Number.MAX_SAFE_INTEGER
9007199254740991
Also, there are few constants in the Math
object (e.g. Math.PI
, Math.E
etc).
-0
6. Catching Note that -0 === +0
is true
. But still, they are doing totally different things:
> 42 / 0
Infinity
> 42 / -0
-Infinity
So, if you ever have to check if it's a negative zero, you can simply make such a division and see what you get.
7. Weird results and expressions
As mentioned above, we do get (negative) Infinity
values when dividing most of the numbers to (negative) zero.
But still, there are few other cases when we get interesting results:
// Dividing zero by zero
0 / 0
→ NaN
// Note there is no -NaN
0 / -0
→ NaN
A rather nice one is 0⁰ which should be mathematically impossible—at least that's what they teach us in school. Well, JavaScript says it's 1, and that's dictated by specs (if the exponent is 0
, the result is 1
always).
Math.pow(0, 0)
→ 1
Math.pow(NaN, 0);
→ 1
Infinity plus anything is still infinity:
Infinity + 42
→ Infinity
Infinity + Infinity
→ Infinity
Though, you get a NaN
if you subtract Infinity
from itself:
Infinity - Infinity
→ NaN
You may be surprised that in JavaScript, 0.1 + 0.2 === 0.3
is false:
0.1 + 0.2 === 0.3
// → false
What's going on here?!
Well, it is due to errors in the floating point precision. Let's take a closer look:
> 0.1 + 0.2
0.30000000000000004
> 0.1 * 0.2
0.020000000000000004
But why is this happening?! It's because of the way computers represent the numbers internally. The short explanation would be: when you divide 1 / 3, you probably round it to 0.333...
. But if you're going to sum 0.333...
three times, you will get 0.999...
instead of 1
(note in this case ...
doesn't represent an infinite series of decimals, but just your precision; btw, X.999999...
—an infinite series of 9
—does equal X + 1
in the real world).
So, it's just because of precision. You'll find better and longer explanations here; and how to solve such problems.
null
equal zero?
Does You'd say no: null === 0 → false
, but let's look at this:
> null < 0
false
> null <= 0
true
> null === 0
false
> null == 0
false
> null >= 0
true
> null > 0
false
So, what's going on here, again?! Well, it's JavaScript.
As first hint, we can check how is null
converted into a number: +null
→ 0
. Since null
converted to a number is 0
, the expressions above make sense—except the strict equal one. The explanation is easy: all the others call the ToPrimitive
operation with a Number
as preferred type.
NaN
(not a number) thingy
8. The NaN
obviously means not a number. Ironically, NaN
is a number (well, it has the number
type):
typeof NaN // "number"
NaN.constructor // Number
There are multiple ways to assign NaN
to a variable:
// Just by using the `NaN` value
let foo = NaN; // NaN
// By parsing a non-number as number
parseInt("foo") // NaN
// Or shorter:
+'foo' // NaN
x !== x
Sometimes Because IEEE 754 dictates that NaN !== NaN
, there we are:
NaN !== NaN
→ true
That's the reason why you should never check if foo
is a not a number by checking if foo === NaN
but by checking if foo !== foo
or isNaN(foo)
.
Do you want to create another similar value by doing the same thing?
Well, we can try something that's almost the same. Let's do it:
// Let's define a property having a getter which returns a random number always
Object.defineProperty(this, "foo", {
get () { return Math.random(); }
});
foo
→ 0.5300470317035524
foo
→ 0.4810690031991778
...
// Now, of course that:
foo !== foo
→ true
However, if you create a variable and assign the foo
's value to it, then it won't work anymore (but it does work with NaN
):
> a = foo
0.2264466197115489
> a === a
true
> a = NaN
NaN
> a === a
false
9. The power of bitwise operators
There are quite a few amazing things you can do with the low level operators which modify the bits of the numbers.
Fast multiplications/divisions
Do you want to multiply/divide a number with powers of 2 (2, 4, 8, 16 etc)? Do you want to make it extremely fast? Then, the answer is to use the <<
operator:
// Same with 3 * 2, but faster
> 3 << 1
6
// Same with 3 * 4
> 3 << 2
12
// Same with 3 * 8
> 3 << 3
24
// Divide by two
> 16 >> 1
8
What happens is that <<
moves all the bits to the left one position. Since the numbers are represented in binary, it adds a new 0
on the right side:
// 5 in base 2:
101
101 << 1
→ 1010 (which is 10 in base 2)
Similar things happen when using the >>
operator.
From my testing, using this <<
is about 1.04
times faster than using the *
operator. Maybe it makes sense when you really care about performance (e.g. rendering some complicated 3D animations).
📝 Note: This doesn't work for non-integers.
Checking if an element is included into an array
Using the ~
(bitwise NOT) bitwise operator to find if an element is included into an array:
let myArray = [1, 2, 42];
if (~myArray.indexOf(42)) {
/* do something */
}
What happens is that there is indexOf
that returns the index of the element (in this case, 2
), or -1
if the element doesn't exist. -1
is the only number for which ~
will return 0
. ~
turns 0
into 1
and 1
into 0
at bit level.
~-1
→ 0
~0
→ 0
~42
→ -43
~-42
~41
Then, obviously, 0
is a false value when converted to boolean, while the other numbers returned by ~
are truly values. So, we can do something like this:
let myArray = [4, 3, 42, 1];
// Get the index:
myArray.indexOf(42)
// → 2
// Let's check if it exists (convert to boolean)
!!~myArray.indexOf(42)
→ true
// ... and one which doesn't exist:
!!~myArray.indexOf(7)
→ false
Cryptography fun
The XOR (^
) operator is really cute. You can encrypt and decrypt messages using it. Here is how it works:
A B ^
=========
0 0 0
0 1 1
1 0 1
1 1 0
This is used in cryptography. A simple example would be encrypting and decrypting a number:
// Alice and Bob know the same secret key:
let key = 42;
// Alice wants to send Bob a number
let msg = 7;
// But before sending it, Alice encrypts it:
msg = msg ^ key // or directly: msg ^= key
→ 45
// Bob receives 45, but knowing the key is 42 he knows to decrypt it:
45 ^ key
→ 7
// Now Bob can enjoy the message from Alice
Wrapping up
Hopefully you enjoyed this read and you learned something! Have fun using numbers for the good of this world. 🚀
I always struggled with accounting concepts until I stumbled upon this service https://domyhomework123.com/accounting . Their approach to explaining balance sheets and income statements is straightforward yet thorough. It’s not just about getting homework done; it’s about truly understanding the subject. I recommend their service for anyone facing difficulties in accounting.
Engaging with the ‘Write My Essay’ service was a turning point in my academic pursuit. As a UK student, their essay writing and proofreading services proved to be indispensable. The meticulous attention to detail and proficiency displayed in crafting my college paper writing service were truly commendable. This service stands out as a reliable and trustworthy option for anyone in need of top-notch assistance. I wholeheartedly recommend their expertise for a seamless and successful essay-writing experience.