Data structures are fundamental tools in programming, enabling us to efficiently store, manipulate, and access data. In JavaScript, a language known for its flexibility, mastering these structures can significantly enhance your ability to solve problems and write optimal code.
In this blog post, we’ll explore commonly used data structures in JavaScript. By understanding both the “how” and the “why” of these data structures, you’ll be better equipped to tackle complex problems. As always, we start simple.
Primitive Types
JavaScript’s primitive types form the foundation of all data manipulation. We can consider that there are 3 main primitive data types: strings, numbers and booleans.
Strings
In JavaScript, strings are a fundamental data type used to represent textual data. A string is essentially a sequence of characters, numbered from 0, for the first character, enclosed within single quotes (’ ’), double quotes (” “), or backticks ( ). Strings are immutable, meaning once created, their content cannot be altered—any modification results in the creation of a new string.
You can create strings in several ways:
- Using Single or Double Quotes
let singleQuoteString = 'Hello, World!';
let doubleQuoteString = "JavaScript is fun!";
Both are functionally identical. The choice is often based on stylistic preference or the need to include quotation marks within the string.
- Using Backticks (Template Literals)
Backticks allow for multiline strings :
let multiline = `This is
a multiline
string.`;
console.log(multiline);
JavaScript uses something called escape sequences to include special characters within strings.
| Escape Sequence | Description |
|---|---|
| \’ | Single quote |
| \” | Double quote |
| \\ | Backslash |
| \n | Newline |
| \t | Tab |
| \b | Backspace |
| \r | Carriage return |
let quote = "She said, \"JavaScript is awesome!\"";
console.log(quote); // She said, "JavaScript is awesome!"
let multiline = "Line1\nLine2\nLine3";
console.log(multiline);
// Output:
// Line1
// Line2
// Line3
JavaScript uses UTF-16 encoding, allowing the use of Unicode characters.
let smiley = "\u263A";
console.log(smiley); // ☺
String manipulation will be addressed in a dedicated blog post.
Numbers
In JavaScript, numbers are a fundamental data type used to represent both integers and floating-point values. JavaScript has two primary numeric types:
- Number (the default type for all numeric values)
- BigInt (for very large integers beyond the safe range of Number)
let integer = 42; // Integer
let float = 3.14; // Floating-point number
let negative = -100; // Negative number
let scientific = 1e6; // Scientific notation (1 * 10^6 = 1000000)
let bigNumber = 1234567890123456789012345678901234567890n;
console.log(bigNumber); // BigInt representation with 'n' at the end
You can represent numbers in different formats:
- Decimal (Base 10):
let decimal = 255;
- Binary (Base 2): Prefixed with 0b
let binary = 0b11111111; // 255 in binary
- Octal (Base 8): Prefixed with 0o
let octal = 0o377; // 255 in octal
- Hexadecimal (Base 16): Prefixed with 0x
let hex = 0xFF; // 255 in hexadecimal
There are some special numeric representations, like Infinity and -Infinity, which represent values beyond the maximum representable number.
console.log(1 / 0); // Infinity
console.log(-1 / 0); // -Infinity
console.log(Infinity + 1); // Infinity
console.log(Infinity); // Infinity
console.log(Infinity + 1); // Infinity
console.log(Math.pow(10, 1000)); // Infinity
console.log(Math.log(0)); // -Infinity
console.log(1 / Infinity); // 0
console.log(1 / 0); // Infinity
Another special representation is NaN (Not-a-Number), which represents an invalid number operation.
console.log("abc" * 3); // NaN
console.log(0 / 0); // NaN
console.log(NaN === NaN); // false (NaN is not equal to itself)
To check for NaN, use:
console.log(isNaN("abc")); // true
console.log(Number.isNaN(123)); // false (preferred, as it's stricter)
Number operations are an extensive subject, covered in a dedicated blog post.
Booleans
In JavaScript, a boolean is a fundamental data type that represents one of two values: true or false. Booleans are essential for controlling program flow through conditional statements, loops, and logical operations. They form the backbone of decision-making processes in programming.
let isJavaScriptFun = true;
let isSkyGreen = false;
console.log(isJavaScriptFun); // true
console.log(isSkyGreen); // false
You can directly assign true or false or obtain boolean results from comparisons and logical operations.
JavaScript can convert other data types to booleans. Use the Boolean() function to explicitly convert values to booleans.
console.log(Boolean(1)); // true
console.log(Boolean(0)); // false
console.log(Boolean("hello")); // true
console.log(Boolean("")); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined));// false
In JavaScript, all values are either “truthy” or “falsy” when evaluated in a boolean context. The following values are considered “falsy” (evaluate to false in boolean contexts):
- false
- 0 and -0
- “” (empty string)
- null
- undefined
- NaN
Everything else is “truthy”, including:
- Non-zero numbers (1, -1, etc.)
- Non-empty strings (“hello”, “0”, etc.)
- Objects ({}, [])
- Functions
Example:
if ("") {
console.log("Truthy!");
} else {
console.log("Falsy!"); // Output: "Falsy!"
}
if ([]) {
console.log("Truthy!"); // Output: "Truthy!" (empty arrays are truthy)
}
NOT (!) Inverts the boolean value: true becomes false and vice versa.
console.log(!true); // false
console.log(!false); // true
let isAvailable = false;
console.log(!isAvailable); // true
Booleans are a fundamental part of JavaScript, enabling conditional logic, control flow, and decision-making. Understanding truthy/falsy values, logical operators, and boolean conversions is crucial for writing clean, efficient code. Whether you’re toggling UI states, validating user input, or controlling program logic, booleans are at the heart of every JavaScript application.
Null and Undefined
In JavaScript, few things are as deceptively simple yet confusing as null and undefined. They both represent the absence of a value, but in subtly different ways. Think of them as distant cousins: related, yet with distinct personalities and behaviors. Understanding when and why to use each is crucial to avoid bugs that lurk in the shadows of type coercion and equality checks.
Undefined Indicates that a variable has been declared but has not been assigned a value. It’s the default value for uninitialised variables and missing function arguments.
let x;
console.log(x); // undefined
Null Represents an intentional absence of any object value. It’s an assignment value, meaning a developer explicitly sets it to indicate “no value.”
let y = null;
console.log(y); // null
Understanding the nuances of null and undefined is like learning the difference between a missing chair and an empty chair. Both suggest “no one’s sitting,” but for entirely different reasons.
Data Structures
Data structures can store collections of values and more complex entities. They are used to manipulate related data in several ways.
Arrays
At its core, an array is a special kind of object designed to hold multiple values in a single, ordered collection. Imagine a row of mailboxes, each assigned a number starting from zero (because JavaScript has a fondness for zero-based indexing). Each mailbox can contain letters, packages, or even a small raccoon if you’re into exotic pets—similarly, JavaScript arrays can hold numbers, strings, objects, functions, or even other arrays.
There are several ways to create an array in JavaScript. Using square brackets is the most common way:
let fruits = ["Apple", "Banana", "Cherry"];
let numbers = new Array(1, 2, 3, 4, 5);
let emptyArray = [];
let fixedArray = new Array(10); // An array with 10 empty slots
Since arrays are zero-indexed, the first element lives at index 0, the second at 1, and so on.
let cars = ["Toyota", "Honda", "Ford"];
console.log(cars[0]); // Outputs: "Toyota"
cars[1] = "Tesla"; // Changing "Honda" to "Tesla"
console.log(cars); // ["Toyota", "Tesla", "Ford"]
If you try to access an index that doesn’t exist, JavaScript politely returns undefined instead of throwing a tantrum.
console.log(cars[5]); // undefined
Arrays are dynamic, meaning you can add or remove elements on the fly, push() adds to the end, unshift() adds to the beginning, pop() removes from the end and shift() removes from the beginning:
cars.push("Chevrolet");
console.log(cars); // ["Toyota", "Tesla", "Ford", "Chevrolet"]
cars.unshift("BMW");
console.log(cars); // ["BMW", "Toyota", "Tesla", "Ford", "Chevrolet"]
let lastCar = cars.pop();
console.log(lastCar); // "Chevrolet"
console.log(cars); // ["BMW", "Toyota", "Tesla", "Ford"]
let firstCar = cars.shift();
console.log(firstCar); // "BMW"
console.log(cars); // ["Toyota", "Tesla", "Ford"]
Need to organize data hierarchically? Arrays can contain other arrays.
let matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
console.log(matrix[1][2]); // Outputs: 6
Arrays are such an extensive and complex topic that they will be addressed again in a dedicated blog post.
Objects
An object is a collection of key-value pairs. The keys (also called properties) are strings (or symbols), and the values can be anything: numbers, strings, functions, arrays, or even other objects.
There are a couple of ways to create objects in JavaScript. Using object literals is the most common way:
let car = {
brand: "Tesla",
model: "Model 3",
year: 2022
};
Here, brand, model, and year are the keys, and “Tesla”, “Model 3”, and 2022 are their respective values.
You can access object properties in two ways: dot notation and bracket notation. Bracket notation is particularly useful when dealing with dynamic property names.
console.log(car.brand); // "Tesla"
console.log(car["model"]); // "Model 3"
let property = "year";
console.log(car[property]); // 2022
You can also add new properties or modify existing ones:
car.color = "red"; // Adding a new property
car.year = 2020; // Updating an existing property
delete car.model; // Removing a property
Objects can contain other objects, allowing you to model more complex data structures:
let rentalCar = {
brand: "Tesla",
model: "Model Y",
owner: {
name: "Alice",
license: "XYZ1234"
}
};
console.log(rentalCar.owner.name); // "Alice"
Objects can do more than just store data—they can also perform actions. As with other complex topics, object swill be a addressed again in their very own, dedicated blog post.
Sets
A Set in JavaScript is like an exclusive party—each value is allowed in only once. No duplicates, no exceptions. It’s a collection of values where uniqueness is the rule, not the exception.
Creating a Set is quite simple:
let uniqueNumbers = new Set([1, 2, 3, 4, 4, 5]);
console.log(uniqueNumbers); // Set(5) {1, 2, 3, 4, 5}
Notice how the duplicate 4 politely disappeared? That’s the magic of Set—it automatically filters out duplicates. You can add values using the add() method:
uniqueNumbers.add(6);
console.log(uniqueNumbers); // Set(6) {1, 2, 3, 4, 5, 6}
To remove a value, use delete():
uniqueNumbers.delete(3);
console.log(uniqueNumbers); // Set(5) {1, 2, 4, 5, 6}
Want to clear the entire set? Just call clear():
uniqueNumbers.clear();
console.log(uniqueNumbers); // Set(0) {}
Sets will also be addressed in another blog post.
Maps
A Map is like a turbocharged object. It lets you use any data type as a key—whether it’s a string, number, object, or even a function. It also remembers the order in which you add items, which makes iteration predictable and intuitive.
You can create a Map as follows:
let carMap = new Map();
carMap.set("brand", "Tesla");
carMap.set("model", "Model 3");
carMap.set("year", 2022);
console.log(carMap);
// Map(3) {"brand" => "Tesla", "model" => "Model 3", "year" => 2022}
Alternatively, you can initialise a Map with an array of key-value pairs:
let userMap = new Map([
["name", "Alice"],
["age", 30],
["isMember", true]
]);
console.log(userMap.get("name")); // "Alice"
Add an entry with set(key, value):
userMap.set("city", "New York");
Retrieve a value with get(key):
console.log(userMap.get("age")); // 30
Remove an entry with delete(key):
userMap.delete("city");
Clear all entries with clear():
userMap.clear();
Unlike objects, Map allows keys of any type:
let objKey = { id: 1 };
let map = new Map();
map.set(objKey, "Object as a key");
console.log(map.get(objKey)); // "Object as a key"
While objects are great for simple, static key-value data, Maps shine when:
- You need keys of any data type.
- The insertion order of items matters.
- You perform frequent additions and deletions of key-value pairs (Maps are faster for these operations compared to objects).
The Map object in JavaScript offers flexibility and performance that objects simply can’t match for dynamic key-value data management. We will be seeing a lot more about maps in a different blog post.
WeakMap and WeakSet
Both WeakSet and WeakMap are specialised and offer a unique feature: they allow for weak references to objects. This means that the garbage collector can automatically remove these objects from memory when they’re no longer needed elsewhere in your code. In simpler terms, they help prevent memory leaks without you having to do any heavy lifting.
A WeakSet is similar to a regular Set, but with a twist: It can only contain objects—no primitive values like strings or numbers. The objects are held weakly, meaning if there are no other references to an object, it can be garbage collected automatically.
let obj1 = { name: "Alice" };
let obj2 = { name: "Bob" };
let weakSet = new WeakSet([obj1, obj2]);
console.log(weakSet.has(obj1)); // true
// Removing external references
obj1 = null; // The object { name: "Alice" } is now eligible for garbage collection
Since WeakSet doesn’t prevent garbage collection, you won’t find methods like size, clear(), or any way to iterate over its elements. It’s designed for specific use cases like tracking objects without worrying about memory leaks.
A WeakMap is like a regular Map, but with some key differences: Keys must be objects (no primitives allowed). Keys are held weakly, meaning if there are no other references to a key, it can be garbage collected along with its associated value.
let user = { id: 1 };
let weakMap = new WeakMap();
weakMap.set(user, "User data");
console.log(weakMap.get(user)); // "User data"
user = null; // The key-value pair is eligible for garbage collection
Just like WeakSet, WeakMap doesn’t support iteration methods (forEach, keys, values, etc.) because the keys can disappear at any moment when garbage collected.
While Set and Map are versatile, they can accidentally cause memory leaks if you forget to manually remove references. WeakSet and WeakMap solve this by design—they automatically let go of objects when they’re no longer needed. They are very specialised and you will probably only use them in very specific situations.
Conclusion
Understanding and leveraging JavaScript’s data structures, along with their real-world applications, is essential for crafting efficient and effective solutions. By matching the right data structure to your problem, you’ll unlock new levels of productivity and maintainability in your code.
Dive into your next project and see how these data structures can simplify your tasks! Happy coding!