Things you did not know about JavaScript
In the world of programming languages, JavaScript is one such language which has evolved fast during the years. It is due to this reason that it is impossible to catch up with all its features and properties. So, let us explore some of the JavaScript features that you may not know existed.
1 Function() Constructor
The function constructor creates a new Function object, and when called directly, creates function objects dynamically. Unlike eval()
, it executes in the global scope only. While creating the function, the function objects created using the Function constructor are parsed. Declaring a function with a function statement or function expression and then calling it within your code is less efficient because such functions are parsed with the rest of the code.
The Function constructor invoked as a function has the same effect as that of invoking it as a constructor without using the new operator.
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
The Functions created with the Function constructor will only be able to access their own local and global variables and not the ones from the scope in which the Function constructor was created.
var x = 10;
function createFunction1() {
var x = 20;
return new Function('return x;'); // this |x| refers global |x|
}
function createFunction2() {
var x = 20;
function f() {
return x: // this |x| refers local |x| above
}
return f;
}
var f1 = createFunctionl();
console.log(f1()); // 10
var f2 = createFunction2();
console.log(f2()); // 20
2 Bitwise Operators
The bitwise operators treat its operands as a sequence of 32 bits (zeroes and one) and not as decimal, hexadecimal, or octal numbers. The decimal number 9 is represented in the binary format as 1001. They perform their operations on such binary numbers but return the standard JavaScript numerical values. We can use these bitwise operators for executing different functions.
Check if an element is part of an array
Using the bitwise NOT operator, the presence of an element in an array can be checked.
let fruits = ['apple', 'pear', 'orange'];
// Usually they do it like this
if (fruits.index0f('pear') !== -1) {
...
}
// That works, but using the `~` makes the code shorter
if (~fruits.index0f('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.
Note*:* If you’re using ES2015, includes will do the job:
['apple', 'pear'].includes('apple')
-> true
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
Faster mathematical operations of integers:
While rendering 3D complex animations on a big canvas, you want to generate and render the frames as quickly as possible. This is where the bitwise operators come into picture in helping with the complex multiplication and division of decimal integers.
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. Using this operator << is about 1.04 times faster than using the * operator.
84 >> 1
> 42
3 Hex/Unicode code:
If you want to hide a string in the code without calling any function, you can do that natively using the 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
'\uee2a'
→ '*'
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\\x6f1\\x20\\x77t‘x61\\x72\\x6c\\x64\\x21'
| awk 'gsub(:\\MM.quot:, ":MquoL)' \x68\x651x6c\x6ctx6f\x20V77\x6f\x72\x6c\x64‘x21
// And finally, we have it!
\x68\x65\x6c\x6c\x6f\x20\x77\x6f\xb72\x6c\x64\x21
> 'hello world!'
4 Two Zeroes:
Usually, we use the positive value of zero, but there is negative -0 as well. They are not seen in close because of the way they are stringified as both (-0).toString()
and (+0).toString()
return 0
+0
→ 0
-0
→ 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 are 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
5 NaN is a Special Number:
Yes, NaN
is a number. There are a few ways we can obtain it:
- 0 / 0 (divide 0 by 0)
- +’foo’ (convert a non-number string into number)
- Infinity – Infinity
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
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.index0f(42);
> 2
// Find the index of NaN (wrong)
values.index0f(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!
myIndex0f(values, NaN)
> 1
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 because this behavior is dictated by IEEE 754.
Every NaN shall compare unordered with everything, including itself.
Note*: If you’re using ES2015, includes handles the NaN case:*
[42, NaN].includes(NaN)
> true
NaN is not (in) finite
NaN is simply neither finite nor infinite.
// Check if it's finit
isFinite(NaN)
→ false
// Comparing with infinity, it will always give us false
Infinity > NaN
→ false
> Infinity < NaN
→ false
-Infinity < NaN
→ false
> -Infinity > Nah
→ false
NaN is neither positive nor negative
NaN is NaN. There are no +NaN or -NaN. Both are NaN.
NaN
→ NaN
-NaN
→ NaN
+NaN
→ NaN
6 Short Circuit Binary Logical Operators
In a binary expression, if the first operand meets the result condition then the second operand is not evaluated.
true && 'hello'
→ 'hello'
// We won't get 'hello', because 'false && anything else' will always be false
false && 'hello
→ false
// Let's try a logical
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'
The same can be used with functions as well:
// Get some random number
let rand = () => Math.candom() + 1;
// Let's create an object to coltect some data
let data = {};
// A function to assign some random numbers to that spectfic key, only the first time
let add = key => !data[key] && (data[key] = rand()) || data[key];
// If 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.4228267915083378722
// 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.4267925083378722,
c: 1.495289320665785
}
7 Running Eval in Strict Mode
eval()
is a dangerous function, which executes the code it's passed with the privileges of the caller. If you run eval()
with a string that could be affected by a malicious party, you may end up running malicious code on the user's machine with the permissions of your webpage / extension. More importantly, a third-party code can see the scope in which eval()
was invoked, which can lead to possible attacks in ways to which the similar Function
is not susceptible.
eval()
is also slower than the alternatives, since it has to invoke the JavaScript interpreter, while many other constructs are optimized by modern JS engines.
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 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);