DEV Community

V
V

Posted on

Defining Primitive and Non-Primitive Data Types in JavaScript

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Let's take a quick look at null.

const dogBreed = null; // object
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

 

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
Enter fullscreen mode Exit fullscreen mode

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?

  1. birdBreed is created and assigned an address in the call stack with the value of 'quail'
  2. favoriteBird is assigned birdBreed as its value, creating a completely new and separate address in the call stack.
  3. 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 }
Enter fullscreen mode Exit fullscreen mode

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...

  1. cup is created in the call stack and is assigned an address.
  2. The value of cup does not contain the object above, it contains another address pointing to a location in the memory heap.
  3. The location in the memory heap contains the value of the cup object.
  4. 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.
  5. 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']
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

 

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)

Collapse
 
vakhramoff profile image
Sergey Vakhramov • Edited

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' 😊