Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Learning

Understanding primitive and reference types in JavaScript

Understanding JavaScript's primitive and reference types is a core concept. In this article, Olasunkanmi dives into the critical information needed to understand the basics.

Olasunkanmi Balogun

·

11 Apr 2024

If you are new to JavaScript, you may not be very familiar with the concept of data types. This may be because JavaScript is a loosely typed language.

Yet, as a JavaScript developer, you use data types regularly but may not completely understand how they work.

In JavaScript, there are two main categories of data types: primitive and reference types. This article breaks down these types, explores their differences, and provides practical examples. Whether you're a beginner or a seasoned developer, this guide will help you gain a solid grasp of JavaScript data types.

Let's begin.

Primitive data types

Primitive types are stored directly in memory and are easy and fast to manipulate, unlike more complex reference types.

A primitive itself is different from a variable assigned a primitive value. For instance:

let a = 5;
console.log(a) //log 5
a = 6
console.log(a) //logs 6

5 in the above code snippet is a primitive but a is just a variable with a primitive value. This means a can be reassigned to a new variable, as seen above, but 5, a primitive, cannot be changed or modified.

In JavaScript, we have seven primitive types:

  1. String
  2. Number
  3. Bigint
  4. Boolean
  5. Undefined
  6. Null
  7. Symbol

Now, let's discuss each of the primitive data types and their characteristics. Note that this article won't discuss the Symbol data type.

String

A string in JavaScript is a basic data type representing a sequence of characters, like words or sentences, stored in memory as UTF-16 character codes. Strings can be made using single quotes ('...'), double quotes ("..."), or backticks(`...`), with backticks being useful for template literals that insert variables or expressions.

Here's how you create and manipulate strings:

let name = 'John' // single quotes
let place = "Portugal" // double quotes
let sentence = `${name} lives in ${place}` // template literals with backticks
console.log(sentence) // John lives in Portugal

String concatenation, using the + operator, lets you join strings:

let firstName = 'John'
let lastName = 'Doe'
let name = firstName + " " + lastName
console.log(name) // 'John Doe'

When a string is concatenated with a number or other value, JavaScript automatically converts it to a string using its toString() method. For instance, 'hello' + 50 becomes 'hello50'.

Number

The number data type in JavaScript handles numeric values, including integers and floating-point numbers. These are represented using the 64-bit floating-point format from the IEEE 754 standard, allowing them to store whole numbers and decimals.

It can hold positive floating-point numbers between 2^1074 and 2^1024 and negative ones between -2^-1074 and -2^1024. For integers, it's safe within the range -(2^53 − 1) to 2^53 − 1, and you can perform various arithmetic operations like addition (+), subtraction (-), multiplication (*), division (/), and modulo (%). Additionally, JavaScript offers built-in math functions such as Math.round(), Math.floor(), and Math.max().

However, it's worth noting that when using these arithmetic operations, JavaScript automatically converts values of other types to numbers. For example:

console.log('4' / '2') // logs 2
console.log('4' * '2') // logs 8

The + operator is an exception, though, as it is used to concatenate two strings, as stated above in the strings section. Hence, '4' + '2' will only concatenate both strings and result in '42'.

Lastly, numbers support Infinity and -Infinity for positive and negative infinity, along with NaN (not a number), which appears when a mathematical operation results in an unrepresentable value.

BigInt

BigInt in JavaScript is a special numeric type that handles large integers. Unlike the regular Number type, BigInt can store numbers much larger than 2^53 − 1 (Number.MAX_SAFE_INTEGER).

Creating a BigInt is easy; just add an n to the end of an integer or use the BigInt() function:

const x = BigInt(Number.MAX_SAFE_INTEGER * 2); // 18014398509481982n
const myBigInt = 1n
console.log(typeof myBigInt); // Output: bigint

Similar to regular numbers, you can use common operators with BigInt, like +, *, -, **, and %:

const x = 1n;
const y = 2n;
const add = x + y // 3n 

However, keep in mind that mixing BigInt values with regular Number values in arithmetic operations will result in a TypeError.

Introduced in ECMAScript 2020, BigInt is relatively new in JavaScript, so be cautious about browser and environment support, especially in production code. Always check compatibility before using it.

Boolean

Boolean in JavaScript deals with logical values, having only two options: true and false.

They're commonly used in conditional statements to determine whether a piece of code should run. For example:

let a = 5;
let b = 6;
let condition = b > a // true

if(condition) {
   console.log("b is greater than a");   
} else {
   console.log("a is greater than b");    
}

Importantly, not all values are automatically considered true or false in JavaScript. Empty strings, zero, and null are evaluated as false, while an empty array or object is considered true:

console.log(Boolean('')) // false
console.log(Boolean(0)) // false
console.log(Boolean([])) // true
console.log(Boolean({})) // true

Undefined

In JavaScript, undefined signifies a value that hasn't been initialized or given a value yet. It's a basic value used when a variable or attribute lacks an assigned value. When a variable is declared but not given a value, it automatically becomes undefined. For example:

let x;
console.log(x) // Output: undefined

Here, x is declared but isn't given a value, so it's undefined.

Keep in mind, undefined is considered a falsy value in JavaScript, meaning it is evaluated as false in boolean contexts. Essentially, think of undefined as a placeholder indicating that a variable hasn't been defined or assigned a value in JavaScript.

Null

In JavaScript, the Null type is simply represented by the value null. It's a primitive value used to signify the intentional absence of an object or when a variable is deliberately assigned no value:

const x = null;
console.log(x); // Output: null

Here, x is explicitly set to null, indicating it intentionally has no object value.

Remember, unlike the undefined type, where the absence of a value is indicated, null denotes the absence of an object.

Similar to undefined, null is considered a falsy value, meaning it evaluates to false in boolean contexts.

Reference data types

Unlike primitive types, reference types in JavaScript are objects. This means they come with properties and methods that can be accessed and modified.

Reference data types aren't directly stored in variables. Instead, variables hold references or pointers to their memory locations. So, when you create a variable for a reference type, it doesn't store the object itself but a reference to it.

Unlike primitive types, reference types can be changed or mutated. Any alteration will be seen in all variables pointing to that object—more on this in the Objects section.

The primary reference types in JavaScript include:

  1. Objects
  2. Arrays
  3. Functions

Objects

In JavaScript, objects are reference data types represented by collections of key-value pairs. You create objects using curly braces, assigning them to variables. For example:

let person = {
  name: "John",
  age: 30,
  city: "New York"
};

You can access properties using dot notation, modify values, and even embed functions as methods. For instance:

console.log(person.name); // "John"
person.age = 31;
person.city = "San Francisco";
console.log(person.age); // 31

When assigning an object to a variable, you store a reference to the object's memory location in the variable under the hood. This allows data and behavior to be shared between variables.

let person1 = {
  name: "John",
  age: 30,
  city: "New York"
};

let person2 = person1;
console.log(person1 === person2); // true

Here, person1 and person2 reference the same object. Changes made through one reference affect the other. As an illustration, examine the below code snippet :

person2.id = 2;
console.log(person1); // { name: "John", age: 30, city: "New York", id: 2}
person1.name = 'James'
console.log(person2); // { name: "James", age: 30, city: "New York", id:2 }

Modifying person2 reflects in person1, and vice versa, as both references point to the same object in memory. This demonstrates that objects in JavaScript are not directly copied but referenced, making changes visible through any reference pointing to the same object.

Arrays

Arrays in JavaScript are a type of reference that enables you to store multiple values in a single variable. Similar to objects, arrays are reference types and can be shared among variables, facilitating shared data and behavior.

Declared with square brackets, arrays can be accessed and modified akin to objects. Here's a quick example:

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

console.log(fruits[0]); // "apple"
console.log(fruits.length); // 3

fruits[1] = "grape";
console.log(fruits); // ["apple", "grape", "orange"]

fruits.push("watermelon");
console.log(fruits); // ["apple", "grape", "orange", "watermelon"]

let removedFruit = fruits.pop();
console.log(fruits); // ["apple", "grape", "orange"]
console.log(removedFruit); // "watermelon"

Arrays efficiently organize and manipulate data collections, providing methods and properties for data management and retrieval. Notably, arrays are zero-indexed, meaning the first element has an index of 0.

Despite being technically objects, arrays in JavaScript have distinct features. While checking their type using typeof, for example console.log(typeof([])) returns "object", arrays offer built-in functionality for indexing, managing lengths, and using array-specific methods. This makes them a potent tool for handling collections of values in JavaScript.

Functions

In JavaScript, functions are a special object type, making them reference types. Functions can be invoked to perform tasks or return values. They're considered reference types because they can be assigned to variables and passed as arguments to other functions.

Here's an example:

// Creating a function
function greet(name) {
  console.log(`Hello, ${name}!`);
}

// Assigning a function to a variable
let myFunc = greet;

// Invoking the function through the variable
myFunc("John"); // Output: Hello, John!

In this snippet, greet is a function assigned to myFunc, establishing a reference to its memory location. This demonstrates how myFunc maintains a connection to the greet function in memory whenever it is called with an argument.

Assigning a function to a variable creates a reference, not a copy. As reference types in JavaScript, functions offer powerful capabilities, supporting higher-order functions, callbacks, and functional programming paradigms.

The differences between primitive and reference types

The difference between primitive and reference types is how they are passed and stored in memory. Let's explore each:

1. Primitive types are passed by value in memory

When you assign a primitive value to a variable, a copy of the value is created and assigned to the new variable. Modifying this copy doesn't affect the original value.

For example:

let num1 = 50;
let num2 = num1;
num2 = 20;
console.log(num1); // Output: 50 (original value remains unchanged)

Primitive data types are stored on a stack, a simple data structure used by the computer to store and retrieve data. Each variable containing a primitive value has its own space on the stack, independent of other variables.

In the code snippet above, the computer allocates space for num1 and stores its assigned value in the stack. Similarly, when num2 is created, the computer reserves additional space on the stack and stores the value 50. The fact that both variables have the same assigned value is irrelevant in this context.

Two variables with different primitive values, representing the JavaScript stack.

As a result, updating num2 won't affect the value of num1 - it remains unchanged.

2. Reference types are passed by reference in memory

Assigning an object or function to a variable creates a reference to the object's memory location. Multiple variables can refer to the same object, and changes through one reference affect others. For example:

let obj1 = { name: "John" };
let obj2 = obj1;
obj2.name = "James";
console.log(obj1.name); // Output: James (change reflected in the original object)

When you declare an object, it is stored in the memory heap, while its reference or pointer is stored on the stack. The pointer is associated with the object's variable name and represents the location of the object in memory, as seen below.

Two variables of reference value pointing to a reference in the heap.

Hence, in the code snippet, obj1 is initially assigned an object with the property name set to "John". When obj2 is assigned the value of obj1, both variables now refer to the same object in the memory heap. Any modifications made to the object through either obj1 or obj2 will be reflected in both variables since they point to the same underlying object.

Understanding pass-by-value and pass-by-reference helps in making informed decisions about memory usage and avoiding unexpected behavior when working with different data types in JavaScript.

Conclusion

Throughout this article, we covered primitive types like boolean, null, undefined, number, and string, as well as reference types like objects, arrays, and functions.

Primitive types are immutable and stored by value, making them straightforward. On the flip side, reference types are mutable and stored by reference, posing unique challenges.

Understanding these nuances equips you to make informed decisions in JavaScript. Knowing how values are passed and manipulated, and understanding the strengths and limitations of each type, empowers you to write more robust and reliable applications.

Practice building projects like a pro

  • Portfolio-ready projects
  • Professional design files
  • Curate your profile
  • Unlimited solution refinement
Unlock Pro

Get all community news and our latest articles in your inbox

Join over 50,000 developers receiving updates via our newsletter. Stay up-to-date with new challenges, articles, community news, featured solutions, and handy links from across the web. We promise no spam, and you can unsubscribe at any time.