What are data types?
Data types specify the kind of information a variable is holding so that the computer knows which computations can be done. Data types do not care exactly “what” the data is, but “how” it can be used.
There are 7 primitive data types in JavaScript
- string
- number
- BigInt
- boolean
- Symbol
- undefined
- null
There is only one non-primitive data type in JavaScript
- object
Identifying JavaScript Data Types Examples
For any of the below values, you can use the typeof operator to return the data type. Try it out yourself!
const bookTitle = "The Great Gatsby";
console.log(typeof bookTitle) // string
Primitive Examples
const automobileMake = 'Honda'; // string
const temperatureInDegrees = 78; // number
const serialNumber = BigInt(83784639236484625) // bigint
const isRunning = true; // boolean
const typeOfSoda = Symbol('Pepsi') // symbol
const computerBrand; // undefined
Let's take a quick look at null.
const dogBreed = null; // object
The null data type will return as type object. Some places reference this as a bug built into JavaScript that cannot be fixed because it will break code that exists today. Other sources indicate that null is closely related to an object (similar to how NaN is related to number), so it was given the object type. Either way it is important to know that while this does return object, it is a PRIMITIVE data type.
Non-Primitive Examples
const employee = {
firstName: 'John',
lastName: 'Smith',
role: 'developer',
}; // object
const colors = ['blue', 'green', 'purple'] // object
What is a Primitive Data Type?
Stored on the Call Stack
When a primitive data type is defined, it is given an address on the Call Stack, rather than the Memory Heap.
Immutable
A primitive data type cannot be changed after its creation, meaning that it is an immutable data type. If we assign a primitive data type a new value, the prior value’s address is still held in memory. The new value is given a different address.
let birdBreed = 'quail';
let favoriteBird = birdBreed;
favoriteBird = 'parrot';
console.log(birdBreed) // quail
Here we are setting favoriteBird equal to birdBreed. When we reassign favoriteBird with a different value, it does not effect the value of birdBreed. This is what we would expect to happen! This is an example showing immutability. You will see later that this is not the case for non-primitive data types.
What's happening behind the scenes?
- birdBreed is created and assigned an address in the call stack with the value of 'quail'
- favoriteBird is assigned birdBreed as its value, creating a completely new and separate address in the call stack.
- favoriteBird is reassigned with the value of 'parrot', which again makes a completely new and separate address in the call stack.
Fixed Size
Primitive data types can only be one size. They cannot grow or shrink in size. They only hold a single value.
const candyName = 'Sour Patch Kids'; // A variable holding a single value of a string
const luckyNumber = 321 // A variable holding a single value of a number
Simple Data Type
Primitive data types cannot be broken down into smaller data types.
const birds = ['parrot', 'quail', 'canary'];
let allTheBirds = '';
birds.map((bird) => {
allTheBirds = allTheBirds + bird
})
console.log(allTheBirds) // parrotquailcanary
Above is an example of how we can take a non-primitive data type and break it down into a primitive type. We are initializing birds and setting it to an array (non-primitive) of birds breeds. From here, we are able to run the map method, which loops over each value in the bird array. Next we are concatenating the bird values into a single string called allTheBirds.
No methods
This topic can get a bit confusing to beginners, because we can perform methods on primitive values.
const text = 'i like singing';
let upperCaseText = text.toUpperCase();
console.log(upperCaseText) // I LIKE SINGING
This is done because of autoboxing. I do not want to go into details on that in this post, but I did want to call it out and point out that this can be done!
What is a Non-Primitive Data Type
Stored on the Memory Heap
A non-primitive data type is assigned an address as a value, which then creates a pointer to that address location in the memory heap.
Mutable
Non-primitive data types are mutable, which means when they are modified, the original copy is modified. Modifying these will not create a copy at a different address. Any direct modifications will effect the same address in the heap.
const cup = {
type: 'plastic',
sizeInOunces: 12,
};
const mug = cup;
mug.type = 'glass';
console.log(cup) // { type: "glass", sizeInOunces: 12 }
Huh?! So what happened? Why when we updated type of the mug object was cup effected? This example shows mutability! Let's take a look behind the scenes...
- cup is created in the call stack and is assigned an address.
- The value of cup does not contain the object above, it contains another address pointing to a location in the memory heap.
- The location in the memory heap contains the value of the cup object.
- mug is assigned the value of cup. Since the value of cup contains an address pointing to a location in the memory heap, mug will also point to that same location in the memory heap.
- Since these are both now pointing to the same location in the memory heap, any changes made to one will effect the other.
Dynamically Sized
Non-primitive data types can change in size. They can grow or shrink and hold multiple values.
const birds = ['quail', 'parrot'];
birds.push('parakeet');
console.log(birds) // ['quail', 'parrot', 'parakeet']
Complex Data Type
Non-primitive data types can be broken down into simpler data types.
Refer to the example under the section "Simple Data Type" of primitive values for an example of this.
Methods
Since non-primitive data types are objects, we can make use of JavaScripts built in methods to simplify our development. We can also create our own object methods by creating an object and assigning a function definition to a property.
Built-in method
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const reversedNumbers = numbers.reverse();
console.log(reversedNumbers); // [9, 8, 7, 6, 5, 4, 3, 2, 1]
Custom method
const dog = {
name: 'Jo',
breed: 'poodle',
color: 'white',
getInformation: function() {
return 'Name: ' + this.name + ', Breed: ' + this.breed + ', Color: ' + this.color;
}
}
console.log(dog.getInformation()) // "Name: Jo, Breed: poodle, Color: white"
Let's do a quick recap
Primitive Data Types
- 7 total in JavaScript
- Stored on the call stack
- Immutable - will be made new in the call stack, never overwrites.
- Cannot grow or expand in size
- Cannot be broken down into a simpler data type
- Cannot use methods (but also remember... autoboxing!)
Non-Primitive Data Types
- 1 total in JavaScript
- Stored in the memory heap
- Mutable - will be modified in the memory heap and overwritten.
- Can grow or expand in size
- Can be broken down into a simpler data type
- Can use methods
Top comments (1)
As I remember, first 3 bits of 32-bit cell pointed to a data type. So, there were — 000: object, 001: integer, 010: double, 100: string, 110: boolean. And 'null' is known as a zero-pointer (0000...0). So, this is how this bug happened and why we see typeof null === 'object' 😊