7 Things You May Not Know in JavaScript

  •  7 Comments

There is a lot of fun to be had when working in JavaScript. Even for developers that interact with it daily, some parts of the language remain unexplored. I’m going to highlight seven things you may not know about JavaScript.

Note: I am going to use ES2015 in the examples: pretty much the let keyword (you can replace it with var if you don’t have an interpreter with ES2015 support) and arrow functions. Alternatively, you can babelify the code automagically.

7 Things You May Not Know in JavaScript

1. There Are Two Zeros

Usually, we use the positive value of zero. But there is a -0 as well! Because of the way how they are stringified, you won’t see them in the console:

+0
→ 0

-0
→ 0

That’s because both (-0).toString() and (+0).toString() return 0.

Going a bit deeper, we find out that they are actually equal:

+0 === -0
→ true

+0 > -0
→ false

+0 < -0
→ false

Also, if we try to use indexOf over arrays that’s not going to help too much (since indexOf does use strict equal: ===):

// We would expect 1, right?
[+0, -0, 42].indexOf(-0)
→ 0

[-0, +0, 42].indexOf(+0)
→ 0

There is a way, actually, to find out if a value is negative zero: by dividing a positive and finite number to that value:

42 / 0
→ Infinity

42 / -0
→ -Infinity

So, we can simply say:

// Two conditions:
//   1. The input parameter should be zero (negative or positive)
//   2. Dividing a number by that input, we should get a negative value (-Infinity)
let isNegativeZero = input => input === 0 && 1 / input < 0;

// Let's test it
isNegativeZero(0)
→ false

isNegativeZero(+0)
→ false

isNegativeZero(-0)
→ true

2. NaN (Not a Number) Is a Special Number

Yes, NaN is a number:

typeof NaN
→ "number"

Thre are a few ways we can obtain it:

  • 0 / 0 (divide 0 by 0)
  • +’foo’ (convert a non-number string into number)
  • Infinity – Infinity
  • …and many more.

Truth is that it is a special number.

NaN does not equal itself.

If you ever saw a thing like if (x !== x) {…}, now you’ll understand what was going on there.

In short, NaN does not equal NaN!

NaN === NaN
→ false

// Even by storing the value in a variable
let x = NaN
x === x
→ false

A side effect is that you’ll never use indexOf if you want to find out the index of a NaN value into an array:

let values = [7, NaN, 42];

// Find the index of 42
values.indexOf(42);
→ 2

// Find the index of NaN (WRONG!)
values.indexOf(NaN)
→ -1


// If you really want to find the index of NaN, simply create your indexOf function
let myIndexOf = (arr, value, start) => {
  if (value !== value) {
    start = start || 0;
    for (let i = start; i < arr.length; ++i) {
       if (arr[i] !== arr[i]) {
          return i;
       }
    }
    return -1;
  }
  return arr.indexOf(value, start);
};

// Now, it will work!
myIndexOf(values, NaN)
→ 1

Note you can use isNaN(x) instead of checking if x !== x to find out if x is NaN, but that’s a little bit slower.

But why is that? The easy answer is that this behaviour is dictated by IEEE 754:

Every NaN shall compare unordered with everything, including itself.

Tip: If you’re using ES2015, includes handles the NaN case:

[42, NaN].includes(NaN)
→ true

NaN is not (in)finite.

NaN is not a finite number. That does not mean it is infinite. It’s simply not finite or infinite.

// Check if it's finite
isFinite(NaN)
→ false

// Comparing with infinity, it will always give us false
Infinity > NaN
→ false

> Infinity < NaN
→ false

-Infinity < NaN
→ false

> -Infinity > NaN
→ false

NaN is neither positive or negative.

NaN is NaN. There are no +NaN or -NaN. Both are NaN:

NaN
→ NaN

-NaN
→ NaN

+NaN
→ NaN

3. Use of Bitwise Operators

This is quite language-agnostic, but since the bitwise operators are supported in JavaScript I’m going to show you some nice stuff using them.

Fast multiplication/division of integers

The use-case could be when you want to render some complex 3D animation on a big canvas. You want to generate and render the frames as quick as possible. Let’s assume you need some math there (surely you do).

When you want to multiply/divide something by 2, 4, 8, 16 or any other power of 2, the trick is to use the bitwise operators which move the bits of the numbers to the right (division) or to the left (multiplication).

// Same with 21 * 2, but faster
21 << 1
→ 42
 
// Same with 5 * 4
5 << 2
→ 20

The << operator moves all the bits to the left one position. The numbers are represented in a binary form, therefore 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.

84 >> 1
→ 42

From my testing, using this << is about 1.04 times faster than using the * operator. Not sure if you want to sacrifice the human-readable code for such a small speed difference. Maybe. Anyway, it’s good to know.

Send encrypted messages

The XOR (^) operator is used in cryptography. 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

A simple example would be encrypting and decrypting a number:

// Alice and Bob share the same secret key:
let key = 123;

// Alice wants to send Bob a number
let msg = 42;

// But before sending it, Alice encrypts it:
msg = msg ^ key // or directly: msg ^= key
→ 81

// Bob receives 45, but knowing the key is 123 he knows to decrypt it:
81 ^ key
→ 42

// Now Bob can enjoy the message from Alice

Find if an element is part of an array

Using the bitwise NOT (~) operator we can check easily if an element appears in an array or not.

let fruits = ["apple", "pear", "orange"];

// Usually they do it like this
if (fruits.indexOf("pear") !== -1) {
   ...
}

// That works, but using the `~` makes the code shorter
if (~fruits.indexOf("pear")) {
   ...
}

This operator turns -1 into 0 (because of its internal binary representation, turning 0 into 1 and 1 into 0), while for other numbers it returns non-zero results. Since 0 is a false value (!!0 → false), that’s why it won’t enter in that if condition.

Tip: If you’re using ES2015, includes will do the job:

["pear", "apple"].includes("apple")
→ true

4. Represent Strings By Using Hex/Unicode Code

If you want, for some weird reason, to hide a string in the code, without calling any functions, you can do that natively. Use wither hexadecimal or unicode escape sequences:

// Hex: '2a' (in base 16) is 42—representing the ASCII code of '*'
'\x2a'
→ '*'

// Unicode: it expects 4 hex digits
'\u002a'
→ '*'

Let’s create a simple function that is supposed to convert human-readable messages:

let strToHex = input => input.split('').map(
    // Convert each character into its hex code
    c => `\x${c.charCodeAt(0).toString(16)}`
).join('');

// Let's convert something.
strToHex('hello world!')
→ '\\x68\\x65\\x6c\\x6c\\x6f\\x20\\x77\\x6f\\x72\\x6c\\x64\\x21'

// Once we have the above output, we just have to remove the escaping:
$ echo '\\x68\\x65\\x6c\\x6c\\x6f\\x20\\x77\\x6f\\x72\\x6c\\x64\\x21' | awk 'gsub("\\\\\\\\", "\\")'
→ \x68\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x21

// And finally, we have it!
\x68\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x21
→ 'hello world!'

5. Short-Circuiting Binary Logical Operators

In a binary expression, the second operand is not evaluated if the first one met the result conditions. Let’s take a look at some examples:

true && 'hello'
→ 'hello'

// We won't get 'hello', because 'false && anything else' will be always false
false && 'hello'
→ false

// Let's try a logical or
false || 'hello'
→ 'hello'

// When we have more than two, they are interpreted in the obvious way (from left to right)
0 && false && 'hello'
→ 0

42 && false && 'hello'
→ false

42 && true && 'hello'
→ 'hello'

Now, the nice thing is that same is applied for functions.

// Get some random number
let rand = () => Math.random() + 1;

// Let's create an object to collect some data
let data = {};

// A function to assign some random numbers to that specific key, only the first time
let add = key => !data[key] && (data[key] = rand()) || data[key];

// Assign a random number to 'a'
add('a')
→ 1.0398168717659242

// Reassigning won't work, but we get back the existing value
add('a')
→ 1.0398168717659242

// Do the same for 'b'
add('b')
→ 1.4267915083378722

// And for 'c'
add('c')
→ 1.495289329665785

// Let's see how the data looks like internally:
// Seems we do have a clean key-value map
{ a: 1.0398168717659242,
  b: 1.4267915083378722,
  c: 1.495289329665785 }

Obviously, the magic is in the add function. Let’s expand this:

// Let's see what's going on here
let add = key => !data[key] && (data[key] = rand()) || data[key];

// Looks more human-readable, but it's longer :D
add = key => {

   // If the value is not yet set...
   if (!data[key]) {
      // set it!
      data[key] = rand();
   }

   // Always, do return the value
   return data[key];
};

6. Running Eval in Strict Mode Is Not That Bad

Yes, eval is evil, but maybe it has some features which can help you in your mad-science projects.

When running eval in strict mode, it doesn’t let you create variables in the surrounding scope. Eval simply interprets and executes the code you pass in:

let x = 35;

// Sum x + 7
let result = eval('x + 7');
→ 42

// You can declare variables there (we are NOT in the strict mode):
eval('var y = x + 7');
console.log(y);
→ 42

Now, when we enable the strict mode, the second eval call above will still work, but will not create the y variable outside of the eval environment:

"use strict";

let x = 35;

eval('var y = x + 7');
//        ^
// ReferenceError: y is not defined
console.log(y);

7. Create Functions Dynamically

We can define dynamic functions using the new Function constructor.

let square = new Function('x', 'return x * x');

// Let's see how it looks like:
console.log(square.toString());
function anonymous(x
/**/) {
return x*x
}

square(4)
→ 16

One use-case when this is used is in templating libraries (such as ejs, ajs etc) which parse the template once and return a function that accepts the template data in it.

Note: You should not use eval or new Function for cases like parsing JSON data, getting the value of a dynamic object key.

Hopefully, you learned something new or at least got a better understanding of what is going on with these JavaScript gems. What other unexplored/uncommon JavaScript features do you know? Share them in the comments.

Newsletter

7 Comments
  1. Michael Lee Sep 22, 4:24 pm

    Really good stuff here Ionică! Especially the bitwise NOT used to check if an element exists in an array. This is the first time I’ve seen that. In a production environment though with other developers, especially newer ones though, do you think that syntax would mess up their understanding of the code? Just curious because I often work with interns who might not understand the NOT syntax. I’ll just send them to your post :)

    Thanks for sharing!

    • Ionică Bizău Sep 23, 6:11 am

      Glad you learned something new!

      I admit it’s a bit unclear for beginners, but still, I won’t say to not use it for that reason. It’s a nice trick to know. `indexOf(…) !== -1` is confusing for beginners as well, I guess.

      I recommend using the `includes` method (part of ES6) and use a polyfill to make sure the method is available: https://github.com/mathiasbynens/String.prototype.includes

  2. test Sep 26, 2:49 pm

    You have an error in code below:
    42 && true && ‘hello’
    -> true

    Must be:
    42 && true && ‘hello’
    -> ‘hello’

  3. Abhinav Kulshreshtha Sep 27, 7:45 pm

    Hi,

    I have a doubt in Bitwise multiplication. It return 42 which I understand is in binary. But how do I get the value ’42’ in usage as replacement of 6. “1.04” performance improvement is useless if time taken to convert that into usable decimal number effectively is than standard multiplication.

    • Ionică Bizău Oct 7, 6:07 pm

      The result of the multiplication is a decimal. It’s faster because it just shifts the bits to the left internally. You don’t have to convert it into decimal.

      The binary numbers used there were for explaining how that works.

      Let me know if you have other questions. 😊

Leave a Reply

*
* Minimum length: 20 characters

Download Web Design Freebies & Resources

psd

Please Confirm Your Email!