JavaScript Objects: The One Thing You Need to Actually Understand
learn how to create, access, update, copy, and loop through objects in JavaScript with real examples and clear diagrams

You probably already know the JavaScript basics: variables to store data, loops to repeat things, functions to wrap logic you want to reuse. That is a solid foundation. But there is one thing sitting right at the core of JavaScript that most beginners either skip over or barely scratch: objects.
Objects are not some advanced topic you get to later. They are one of the fundamental data types in JavaScript, and here is the thing that makes them different from the rest: in JavaScript, almost everything is an object. Arrays are objects. Functions are objects. Even the built-in things you use every day, like Math or Date, are objects. So if you actually sit down and understand how objects work, a huge chunk of JavaScript starts making sense in a way it did not before.
This post covers everything you need to get comfortable with objects: what they are, how to create them, how to read and update them, and how to loop through them. No fluff, just the stuff that actually matters.
TLDR
An object stores data as key-value pairs, like a labeled box where each label is the key and the content is the value
Create one with curly braces:
let person = { name: "Virat", age: 35 }Read values with dot notation (
person.name) or bracket notation (person["name"])Use bracket notation when the key comes from a variable
Add or update properties by assigning to them:
person.city = "Mumbai"Remove a property with the
deletekeywordObjects can be nested inside other objects, access deep values with chained dot notation:
person.address.cityAccessing a key that does not exist returns
undefined, not an errorUse the
inoperator orhasOwnProperty()to safely check if a property existsAssigning one object to another variable does not copy it, both variables point to the same object in memory
Use the spread operator
{ ...obj }to make a shallow copy (top level only, nested objects are still shared)Use
structuredClone(obj)for a deep copy that duplicates everything at every level, fully independentUse
Object.entries()to loop through both keys and values at the same timeIn JavaScript, almost everything under the hood is an object, so getting this right opens up a lot
Why Objects Exist in the First Place
Let's say you want to store information about a person. You know their name, their age, and the city they live in. Without objects, you might reach for variables:
let name = "Virat";
let age = 35;
let city = "Mumbai";
This works, barely. But the moment you need to handle a second person, things fall apart:
let name1 = "Virat";
let age1 = 35;
let city1 = "Mumbai";
let name2 = "Deepika";
let age2 = 28;
let city2 = "Delhi";
You are basically duct-taping data together with naming conventions. That is not structure, that is chaos with labels.
Objects solve this by letting you group related data under one roof. Instead of three separate variables for a person, you have one thing called a person that holds all three.
What an Object Actually Looks Like
An object is a collection of key-value pairs. Each piece of data has a name (the key) and a value. That's the whole idea.
Here is what that looks like visually:
+---------------------------+
| person |
+------------+--------------+
| Key | Value |
+------------+--------------+
| name | "Virat" |
| age | 35 |
| city | "Mumbai" |
+------------+--------------+
And in actual JavaScript:
let person = {
name: "Virat",
age: 35,
city: "Mumbai"
};
The curly braces {} wrap the whole thing. Each entry is a key, followed by a colon, followed by the value. Entries are separated by commas. That is the syntax, nothing more to it.
Objects vs Arrays: Which One Do You Need?
This is a question beginners run into all the time, so let's clear it up right here.
Arrays are for ordered lists of similar things. Objects are for describing one thing with multiple properties.
A list of city names? Use an array. Information about one city, like its name, population, and country? Use an object. If you are describing a single thing with different attributes, that is an object.
Creating an Object
There are a couple of ways to create objects. The most common is the object literal, which is what we already wrote above:
let person = {
name: "Virat",
age: 35,
city: "Mumbai"
};
You can also start with an empty object and add to it later:
let person = {};
person.name = "Virat";
person.age = 35;
person.city = "Mumbai";
Both end up giving you the same result. Which one you use depends on whether you know all the data upfront or not.
Nested Objects
So far every value in our object has been a string or a number. But a value can also be another object. This is called nesting, and it comes up constantly once you start working with real data.
Think about Virat Kohli's address. An address itself has multiple pieces: a city, a state, a pincode. It makes more sense to group those together than to dump them all at the top level of the person object:
let person = {
name: "Virat",
age: 35,
address: {
city: "Mumbai",
state: "Maharashtra",
pincode: "400001"
}
};
Here is what that structure looks like:
person
├── name → "Virat"
├── age → 35
└── address → (object)
├── city → "Mumbai"
├── state → "Maharashtra"
└── pincode → "400001"
To get to a nested value, you just chain the dot notation:
console.log(person.address.city); // Mumbai
console.log(person.address.pincode); // 400001
You are reading it left to right. First get person, then get address from it, then get city from that. Each dot goes one level deeper.
This also works with bracket notation if needed:
console.log(person["address"]["city"]); // Mumbai
One thing to be careful about: if an intermediate key does not exist, chaining will throw an error. If person.address is undefined, trying to read person.address.city will crash your code. We will talk about how to safely check for that in the section below.
Accessing Properties
Once you have an object, you need to pull data out of it. There are two ways to do that.
Dot Notation
This is the one you will use most of the time. Clean and readable:
let person = {
name: "Virat",
age: 35,
city: "Mumbai"
};
console.log(person.name); // Virat
console.log(person.age); // 35
console.log(person.city); // Mumbai
You write the object name, a dot, and then the key you want. Simple.
Bracket Notation
This one looks a bit different. You pass the key as a string inside square brackets:
console.log(person["name"]); // Virat
console.log(person["city"]); // Mumbai
It looks more verbose, but it is useful in specific situations. The biggest one is when you do not know the key name ahead of time and it comes from a variable:
let field = "age";
console.log(person[field]); // 35
With dot notation, person.field would look for a key literally named field, not the value of the variable. Bracket notation handles that correctly.
Updating Properties
Updating a value is just as simple as reading one. You access the property and assign a new value to it:
let person = {
name: "Virat",
age: 35,
city: "Mumbai"
};
person.age = 36;
console.log(person.age); // 36
The object now holds the updated value. The original is gone. This works the same way whether you use dot or bracket notation:
person["city"] = "Bangalore";
console.log(person.city); // Bangalore
Adding New Properties
You are not locked into the properties you defined when you created the object. You can add new ones at any point:
let person = {
name: "Virat",
age: 35
};
person.city = "Mumbai";
person.isStudent = false;
console.log(person);
// { name: "Virat", age: 35, city: "Mumbai", isStudent: false }
JavaScript does not complain. It just adds the property to the object. This flexibility is one of the things that makes objects easy to work with as your data grows.
Deleting Properties
Sometimes you want to remove a property entirely. JavaScript gives you the delete keyword for that:
let person = {
name: "Virat",
age: 35,
city: "Mumbai"
};
delete person.city;
console.log(person);
// { name: "Virat", age: 35 }
After deletion, accessing person.city gives you undefined. The key no longer exists in the object.
undefined, null, and Missing Keys
This is one of those things that trips up almost everyone at some point, so let's address it directly.
When you try to access a property that does not exist on an object, JavaScript does not throw an error. It returns undefined:
let person = {
name: "Virat",
age: 35
};
console.log(person.city); // undefined
console.log(person.nickname); // undefined
This is fine for reading, but if you try to go deeper into something that is undefined, you will get an error:
console.log(person.address.city);
// TypeError: Cannot read properties of undefined
person.address is undefined, and you cannot read .city off of undefined. This is one of the most common runtime errors in JavaScript.
There is also null, which is different. null means someone explicitly set that value to nothing. undefined means the key was never there to begin with:
let person = {
name: "Virat",
nickname: null // intentionally empty
};
console.log(person.nickname); // null (set on purpose)
console.log(person.city); // undefined (never existed)
Checking if a Property Exists
Rather than accessing a key blindly and hoping it is there, you can check first. The in operator tells you whether a key exists on an object, regardless of its value:
let person = {
name: "Virat",
age: 35,
city: "Mumbai"
};
console.log("name" in person); // true
console.log("nickname" in person); // false
You can also use hasOwnProperty(), which does the same thing but is more explicit:
console.log(person.hasOwnProperty("age")); // true
console.log(person.hasOwnProperty("sponsor")); // false
Both work. The in operator is shorter and more commonly seen in modern code. Use whichever reads more naturally to you:
if ("city" in person) {
console.log(person.city);
}
// Mumbai
This pattern, check before you access, will save you from a lot of unexpected crashes.
Looping Through an Object
Arrays have a length and a natural order, so looping through them is straightforward. Objects are different. They do not have a numeric index or a built-in order you can iterate over with a regular for loop.
Instead, you use for...in:
let person = {
name: "Virat",
age: 35,
city: "Mumbai"
};
for (let key in person) {
console.log(key, ":", person[key]);
}
// name : Virat
// age : 35
// city : Mumbai
On each iteration, key holds the name of the current property as a string. To get the value, you use bracket notation with that key variable.
If you just want the keys as an array, or just the values, JavaScript gives you two handy methods:
console.log(Object.keys(person));
// ["name", "age", "city"]
console.log(Object.values(person));
// ["Virat", 35, "Mumbai"]
These are cleaner when you only need one side of the pair.
But most of the time you want both the key and the value together. That is exactly what Object.entries() is for. It gives you an array of pairs, where each pair is [key, value]:
console.log(Object.entries(person));
// [["name", "Virat"], ["age", 35], ["city", "Mumbai"]]
You can loop over those pairs directly, which makes your loop a lot cleaner:
for (let [key, value] of Object.entries(person)) {
console.log(key + ": " + value);
}
// name: Virat
// age: 35
// city: Mumbai
The [key, value] part is called destructuring. It pulls the first item of each pair into key and the second into value automatically. This is the pattern you will see most often in real codebases when someone needs to iterate over an object.
Copying an Object vs Referencing It
This is the one that catches almost every beginner off guard, usually at the worst possible time.
When you assign an object to a new variable, you are not making a copy. Both variables end up pointing to the exact same object in memory:
let player1 = { name: "Virat", score: 89 };
let player2 = player1;
player2.score = 0;
console.log(player1.score); // 0
console.log(player2.score); // 0
You changed player2.score, but player1.score changed too. That is because they are the same object. You just gave it two names.
Here is a diagram of what is actually happening in memory:
let player1 = { name: "Virat", score: 89 }
let player2 = player1
VARIABLES MEMORY
+---------+ +-------------------------+
| player1 | -----> | { name: "Virat", |
+---------+ | score: 89 } |
| player2 | -----> | |
+---------+ +-------------------------+
Both point to the SAME object.
Changing one changes both.
To make an actual independent copy, use the spread operator:
let player1 = { name: "Virat", score: 89 };
let player2 = { ...player1 };
player2.score = 0;
console.log(player1.score); // 89 (untouched)
console.log(player2.score); // 0
The { ...player1 } syntax spreads all the key-value pairs from player1 into a brand new object. Now they are truly separate:
let player2 = { ...player1 }
VARIABLES MEMORY
+---------+ +-------------------------+
| player1 | -----> | { name: "Virat", |
+---------+ | score: 89 } |
+-------------------------+
+---------+ +-------------------------+
| player2 | -----> | { name: "Virat", |
+---------+ | score: 0 } |
+-------------------------+
Two separate objects in memory.
Changing one does NOT affect the other.
You can also use spread to merge two objects into one:
let basics = { name: "Virat", age: 35 };
let stats = { runs: 27000, centuries: 80 };
let fullProfile = { ...basics, ...stats };
console.log(fullProfile);
// { name: "Virat", age: 35, runs: 27000, centuries: 80 }
Shallow Copy vs Deep Copy
You can also use spread to merge two objects into one:
let basics = { name: "Virat", age: 35 };
let stats = { runs: 27000, centuries: 80 };
let fullProfile = { ...basics, ...stats };
console.log(fullProfile);
// { name: "Virat", age: 35, runs: 27000, centuries: 80 }
Now here is where spread has a real limitation you need to know about.
Spread only copies one level deep. If your object has a nested object inside it, the nested part is still shared by reference. This is called a shallow copy.
let player1 = {
name: "Virat",
stats: { runs: 27000, centuries: 80 }
};
let player2 = { ...player1 };
player2.name = "Rohit"; // fine, top-level string gets copied
player2.stats.runs = 0; // danger: this affects player1 too
console.log(player1.name); // "Virat" (untouched)
console.log(player1.stats.runs); // 0 (changed! stats is still shared)
Here is what is actually happening in memory:
SHALLOW COPY with spread
player1 player2
+------------------+ +------------------+
| name: "Virat" | | name: "Rohit" |
| stats: ---------+------------>| stats: ---------+--+
+------------------+ +------------------+ |
|
+---------v--------+
| runs: 27000 |
| centuries: 80 |
+------------------+
Top-level values are copied.
Nested objects are still SHARED.
The top-level string name got copied properly. But stats is an object, so both player1.stats and player2.stats still point to the same thing in memory. Changing one changes both.
Deep Copy with structuredClone
To copy an object at every level, including all nested objects, you need a deep copy. The modern and cleanest way to do this in JavaScript is structuredClone():
let player1 = {
name: "Virat",
stats: { runs: 27000, centuries: 80 }
};
let player2 = structuredClone(player1);
player2.name = "Rohit";
player2.stats.runs = 0;
console.log(player1.name); // "Virat" (untouched)
console.log(player1.stats.runs); // 27000 (untouched)
console.log(player2.name); // "Rohit"
console.log(player2.stats.runs); // 0
Now they are completely independent at every level:
DEEP COPY with structuredClone
player1 player2
+------------------+ +------------------+
| name: "Virat" | | name: "Rohit" |
| stats: ------+ | | stats: ------+ |
+---------------+--+ +---------------+--+
| |
+----------v-------+ +----------v-------+
| runs: 27000 | | runs: 0 |
| centuries: 80 | | centuries: 80 |
+------------------+ +------------------+
Everything is copied at every level.
No shared references anywhere.
structuredClone is built into modern JavaScript, no libraries needed. It handles nested objects, arrays inside objects, and most common data types you will encounter day to day.
To summarise when to use each approach:
Assignment → let b = a No copy at all. Same object, two names.
Shallow copy → let b = { ...a } Copies top level only. Nested objects still shared.
Deep copy → structuredClone(a) Copies everything at every depth. Fully independent.
For simple flat objects, spread is fine. The moment your object has anything nested inside it, reach for structuredClone.
A Complete Example
Let's put everything together in one place. Here is a small script that creates an object, updates it, adds to it, loops through it, and deletes from it:
// Create the object
let person = {
name: "Virat",
age: 35,
city: "Mumbai"
};
// Access properties
console.log(person.name); // Virat
console.log(person["age"]); // 35
// Update a property
person.age = 36;
console.log(person.age); // 36
// Add a new property
person.email = "virat@example.com";
console.log(person.email); // virat@example.com
// Loop through all keys and values
for (let key in person) {
console.log(key + ": " + person[key]);
}
// name: Virat
// age: 36
// city: Mumbai
// email: virat@example.com
// Delete a property
delete person.email;
console.log(person);
// { name: "Virat", age: 36, city: "Mumbai" }
Read through this slowly. Every concept from this post is in there, and together they show you the full lifecycle of working with an object.
Assignment: Build a Student Object
Time to try it yourself. Here is what to build:
Create an object called
studentwith the following properties:name,age, andcoursePrint the student's name to the console
Update the
ageproperty to a different valueAdd a new property called
gradeUse a
for...inloop to print every key and value in the object
Here is a starter if you need it:
let student = {
name: "Deepika",
age: 38,
course: "Film Studies"
};
// Your code below
Work through it step by step. If something does not behave the way you expect, log the whole object with console.log(student) and see what is actually inside it. That alone will debug most problems.
Wrapping It Up
Objects are one of those things that feel small when you first learn them and then turn out to be everywhere. Get comfortable with the basics here, and a lot of JavaScript will start making more sense on its own. The rest, prototypes, classes, all of it, builds directly on what you just read.