JavaScript Data Types
Tags: javascript, beginners, webdev
Published: 2020-08-18 16:02:18

In this post, I will explain each Javascript data type. I will focus on a simple definition of each data type and some characteristics and problems about them.

# Primitive Data Types

A primitive data type is an immutable data that doesn't have either properties or methods.

There are 7 primitive types:

`undefined`, `null`, `string`, `number`, `boolean`, `symbol` (ES5), `bigint` (ES11)

Before I explain each type, I would like to introduce the **typeof** operator.

# Typeof operator

To know the type of any value, you can use the **typeof** operator. The typeof operator returns a string which indicates the type of the parameter value.

```jsx
typeof("Hola") // string
typeof(12) // integer
typeof(true) // boolean
typeof(undefined) // undefined
typeof(null) // object
typeof({}) // object
typeof(Symbol()) // symbol
typeof(1n) // bigint
typeof(function(){}) // function
```

In the previous example, there are two cases in which typeof operator doesn't return the expected data type:

- `typeof(null) = 'object'`

- `typeof(function(){}) = 'function'`

Later, I will explain the reason of this behavior.

# Explaining each Primitive Data Type

## Undefined

It is a data type which represents a non-defined value. It means that you declare a variable but you don't set a value. The variable will contain an `undefined` value.

The typeof operator with an undefined value or with a non-declared variable returns 'undefined'.

```jsx
var a;
console.log(a); // undefined
console.log(typeof(a)) // 'undefined'
console.log(typeof(b)) // 'undefined'
console.log(b) // Uncaught ReferenceError: b is not defined
```

If you want to clean a variable, you can set `undefined`, but it's not a good practice. For this purpose you should use the data type `null`.

## Null

It is a data type which represents the absence of value. It means that a variable contains nothing or you don't know its value yet.

You should set `null` instead of `undefined` when you want to indicate that the variable is defined but without a value.

```jsx
const a = null;
console.log(a); // null
console.log(typeof(a)); // 'object'
```

Something interesting with `null` is that `typeof(null) = 'object'`

This is an unexpected behavior because `null` is a primitive data type, so typeof(null) should be `null`. It's a bug from the beginning of the language. There was a proposal to fix this bug but it was rejected because this fix would break a lot of existing deployed code. Here the link of the proposal: https://web.archive.org/web/20160331031419/http://wiki.ecmascript.org:80/doku.php?id=harmony:typeof_null

## String

It's a data type which represents text. It is used to store and manipulate text.

You can define a `string` using single quotation marks, double quotation marks or backticks. With backticks, you can concatenate and interpolate variables inside the string.

```jsx
const name = 'Julian'; // Single quotation marks
const lastname = "Scialabba"; // Double quotation marks
const hello = `Hello ${name} ${lastname}`; // Backsticks -> Hello Julian Scialabba
console.log(typeof(hello)); // 'string'
```

String is a primitive data type. It means that strings don't have methods. But, why can we do something like that?:

```jsx
const name = 'Julian';
name.length // 6 
name.toUpperCase(); // JULIAN  
//Why this string primitive has attributes and methods?
```

You can see that our string primitive is behaving like an object because it has properties and methods. And that is what really happens because it is an object. JavaScript internally creates a wrapper object that provides functionalities to our string primitive. JavaScript has wrapper objects for all primitives data types except for `null` and `undefined`. For this reason, `null` and `undefined` don't have methods and we usually find errors like:

```jsx
var a;
var b = null;
var c = 'Hello';
console.log(a.toString()); // TypeError: a is undefined
console.log(b.toString()); // TypeError: b is null
console.log(c.toString()); // Hello
```

You can also create a wrapper object on your own:

```jsx
const name = String('Julian');
console.log(typeof(name)); // 'string'
```

This way of creating a String is useful because it allows us to cast values. For example:

```jsx
const numberStr = String(123);
const boolStr = String(true);
console.log(numberStr) // '123'
console.log(boolStr) // 'true'
```

You can also create a wrapper object with 'new' operator:

```jsx
const name = new String('Julian');
console.log(typeof(name)); // 'object'
```

But, this way of creating a primitive value is not recommended. It is inefficient and it could cause confusion because the type of the variable is 'object' instead of 'string'.

You can create strings through Javascript type coercion, too. For example:

```jsx
const age = 29;
const ageFromWrapperInteger = age.toString() // '29'
const ageCoercionStr = 29 + ''; // '29'
const isMaleCoercionStr = true + ''; // 'true'
console.log(typeof(ageFromWrapperInteger)); // 'string'
console.log(typeof(ageCoercionStr)); // 'string'
console.log(typeof(isMaleCoercionStr)); // 'string'
```

## Number

It's a data type which represents numbers with or without decimals. It is used to store and operat numbers.

You can define a `number` using numbers without quotes or with the number wrapper object.

```jsx
const number = 29;
const numberWithDecimals = 3.14;
const numberObjectWrapper = Number('2'); // 2
typeof(number); // 'number'
typeof(numberWithDecimals); // number
typeof(numberObjectWrapper); // number
```

Numbers in Javascript are encoded as double precision floating point numbers, following the international IEEE 754 standard. This format uses 64 bits to store the numbers. As a result of this representation, we have to consider two situations when we work with numbers:

1. Safe integers
2. Operations between decimal numbers

####Safe Integers####

Numbers in JavaScript have a safe range in which you can operate accurately:

`[Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER] = [-2**53+1, 2**53-1] = [-9007199254740991, 9007199254740991]`

If you operate outside the limits, your operations won't have precision and some numbers might not be represented. For example:

```jsx
const maxSafe = Number.MAX_SAFE_INTEGER; //9007199254740991
const x = maxSafe + 1;  //9007199254740992
const y = maxSafe + 2;  //9007199254740992
x === y // true
```

ES11 specification added a new primitive data type to handle numbers larger than `2**53 - 1`. It is called BigInt and I will explain it later.

####Operations between decimal numbers####

Numbers in JavaScript are stored in base-2 (binary) system. There are some decimal fractions that can't be represented exactly as binary fractions. For that reason, we have problems like the following:

```jsx
0.2 + 0.1 // 0.30000000000000004
0.2 - 0.3 // -0.09999999999999998
```

You should be careful when you operate with fractions and when you need precision on your operations. To handle this problem, we have some options:

#####Work with a library:

If you need precision, you can work with a library, like [Big Decimal](https://github.com/royNiladri/js-big-decimal), that handles this problem.

#####Round numbers:

You can round numbers with the method `Number.toFixed(digits)`

```jsx
const number = 0.1 + 0.2; //// 0.30000000000000004
const numberFixedStr = number.toFixed(2); // '0.30'
const numberFixed = +number.toFixed(2); // 0.3
```

You can see that `toFixed` returns a string. If you want to get an integer number, you have to     write a `+` at the beginning of the result (it coerces from String to Number types).

#####Work with integers:
Another option is to transform all our decimal numbers to integer numbers. Then you operate with integers, and finally you return the decimal number. For example:

```jsx
const number1 = 0.1 * 100;
const number2 = 0.2 * 100;
(number1 + number2) / 100; // 0.3
```

####NaN: Not a Number####

The `NaN` value is a number that represents invalid numbers. There is a method `isNaN(value)` that indicates if a value is a `NaN`.

```jsx
const x = 0/0; // NaN
const y = 1 * undefined; // NaN
const z = Number('Sarasa'); // NaN
typeof(x); // 'number'
typeof(y); // 'number'
typeof(z); // 'number'
x === y; // false
NaN === NaN // false
isNaN(x); // true
```

The value `NaN` isn't equal to any value, not even to `NaN`.

## Boolean

It's a data type which represents the two boolean values: `true` or `false`.

```jsx
const isValid = true;
const isFetching = false;
const isLoading = Boolean(true);
typeof(isValid); // 'boolean'
typeof(isFetching); // 'boolean'
typeof(isLoading); // 'boolean'
```

You can create boolean values as in the previous example or you can create boolean values with:

- Comparison and logical operators 
- Truthy and falsy values.

####Comparison and logical operators

The comparison and logical operators are used to test any value data type and return `true` or `false`.

#####Comparison Operators:

- Equal value: `==`
- Equal value and type: `===`
- Not equal value: `!=`
- Not equal value and not equal type: `!==`
- Greather than: `>`
- Less than: `<`
- Greather than or equal to: `>=`
- Less than or equal to: `<=`

Examples:

```jsx
'123' == 123; // true
'123' === 123; // false
'123' != 123; // false
'123' !== 123; // true
123 > 123; // false
123 >= 123 // true
123 < 100 // false
123 <= 122 // true
```

#####Logical Operators

- Not: `!`
- And: `&&`
- Or: `||`

Examples:

```jsx
!true // false
true && false // false
true || false // true
true && !false // true
```

Comparison and Logical Operators can be used in conditional statements:

```jsx
const isValid = true;
const isFetching = false;
const name = '';
if(isValid && !isFetching){
 console.log('Fetch values');
}
if(!name) {
 console.log('User without name');
}
```

In the first `if` you can see that the condition is based on two boolean values. However, in the second `if` the condition is a String value. Why can we use any data type inside the conditional statement? It is due to Javascript type coercion and truthy and falsy values.

####Falsy and truthy values

Javascript type coercion is the implicit conversion of values from one data type to another. This conversion is done automatically by the Javascript engine. Here, some examples:

```jsx
'1' + 1 // '11' -> Converts Number to String and concat strings
'1' - 1 // 0 -> Converts String to Number and substract numbers
'1' * 1 // 1 -> Converts String to Number and multiply numbers
'1' + true // '1true' -> Converts Boolean to String and concat strings
1 + true // 2 -> Converts Boolean to Number and sum numbers. True = 1
1 + false // 1 -> Converts Boolean to Number and sum numbers. False = 0
'1' - true // 0 -> Converts String and Boolean to Numbers and substract numbers 
```

If a value is inside of a conditional statement, it will be coerced as boolean type. The values that are coerced as `true` are called 'Truthy Values' and the values that are coerced as `false` are called 'Falsy Values'. 
All the values that are not falsy are truthy values. For this reason, we only have to know the falsy values:

- Number zero: `0`
- BigInt zero: `0n`
- Empty string: `''`
- Null: `null`
- Undefined: `undefined`
- Not a Number: `NaN`

```jsx
Boolean(0) // false
Boolean(0n) // false
Boolean('') // false
Boolean(null) // false
Boolean(undefined) // false
Boolean(NaN) // false
Boolean({}) // true -> Empty object is truthy
Boolean([]) // true -> Empty array is truthy
Boolean('Hello') // true
```

## Symbol

It's a data type that was introduced in ES5 and represents an unique identifier. It can be created using `Symbol()` with an optional string as parameter. 

```jsx
const aSymbol = Symbol();
const symbolId = Symbol('id');
const anotherSymbolId = Symbpl('id');
typeof(aSymbol) // 'symbol'
symbolId === anotherSymbolId // false
```

Each symbol has to be unique. For this reason, the equal comparison between two symbols with the same description is `false`. Every time you create a `Symbol()`, you will get a new and unique symbol that will be different from others.

```jsx
Symbol() === Symbol() // false
```

Symbols are used to:

1. Identify unique and hidden properties on objects.
2. Identify a list of unique constant values.
3. Implement Well-Known Symbols to change some characteristics of the language: [https://tc39.es/ecma262/#table-1](https://tc39.es/ecma262/#table-1)

## BigInt

It's a data type that was introduced in ES11. It represents numbers larger than `2**53 - 1`, which is the limit for the primitive data type number. BigInt provides support for integers of arbitrary length but not for decimal values.

A BigInt can be created adding an `n` at the end of the number or with the wrapper object `BigInt(value)`

```jsx
const bigIntValue = BigInt(1); // 1n
const anotherBigIntValue = BigInt('9999') // 9999n
bigIntValue + anotherBigIntValue // 10000n
const maxSafeInteger = 9007199254740991n; // MAX_SAFE_INTEGER
const maxSafeInteger1 = maxSafeInteger + 1n; // 9007199254740992n
const maxSafeInteger2 = maxSafeInteger + 2n; // 9007199254740993n -> With numbers we can't get this value
maxSafeInteger1 !== maxSafeInteger2 // true
typeof(maxSafeInteger) // 'bigint'
```

The available operators for BigInt are: `+, *, -, **, /, %`. You can't operate between a BigInt and a Number with those operators. You can't either operate with the `Math` library because this library uses data type Number.

```jsx
const maxSafeInteger = 9007199254740991n;
maxSafeInteger + 9n // 9007199254741000n
maxSafeInteger - 1n // 9007199254740990n
maxSafeInteger * 2n // 18014398509481982n
maxSafeInteger ** 2n // 81129638414606663681390495662081n
5n / 2n // 2n -> It is rounded towards 0 because BigInt doesn't represent decimals
maxSafeInteger % 2n // 1n
maxSafeInteger + 2 // can't convert BigInt to number
Math.pow(3n,2) // can't convert BigInt to number
```

Comparisons between BigInt and Number work fine:

```jsx
3n > 2 // true
1n > 2 // false
2n >= 2 // true
2n == 2 // true
2n === 2 // false -> The types are different
```

And finally, the only falsy value of BigInt is `0n`. 

```jsx
Boolean(0n) // false
Boolean(1n) // true
```

It's not recommended that you coerce BigInt to Number. If you need numbers lower than `2**53 -1`, you should use Number data type. If you need numbers higher than `2**53 -1`, you should use BigInt data type. 

If you coerce a BigInt higher than `2**53 -1`  to number, you might loss precision. 

```jsx
Number(1n) //1
Number(9007199254740993n) //9007199254740992
```

# Object Data Type

In JavaScript, every value that is not a primitive data type is an object data type. 

Object data type is used to represent values with properties and methods. While primitive data types represent a simple immutable value, an object data type represent collection of data and more complex entities.

The typical Javascript key-value structure is an object data type. 

```jsx
const person = {
  name: 'Julian',
  lastname: 'Scialabba'
};
typeof(person); // 'object'
```

Other structures like Array, Function, Set, HashMap, Date, among others, are objects data type, too.

```jsx
const numbers = [1, 2, 3, 4];
const aSet = new Set();
const aDate = new Date();
const sayHello = function(name){
  console.log(`Hello ${name}!`);
}
typeof(numbers) // 'object'
typeof(aSet) // 'object'
typeof(aDate) // 'object'
typeof(sayHello) // 'function'
```

We might expect that `typeof(function(){}) = 'object'` but instead is `typeof(function(){}) = 'function'` . It can be explained because ECMAScript specification defines typeof operator as follows:

| Typeof Val   |      Result      |
|----------|:-------------:|
| Undefined |  'undefined' |
| Null      |    'object'   |
| Boolean | 'boolean' |
| Number | 'number' |
| String | 'string' |
| Object (Not implement [[Call]]) | 'object' |
| Object (Implement [[Call]]) | 'function' |

So, `typeof(function(){}) = 'function'` because function is an object that implements [[Call]].

# Conclusion

I have explained every primitive data type and the object data type. I have focused on a simple definition of each data type and some characteristics and problems about them.

I hope that you like this post! Let me know your thoughts on the comments. 

Thanks for reading!