An object groups related data as key/value pairs — a way to keep everything about one thing together under a single name. You write it as a literal inside curly braces:
const book = {
title: "Dune",
pages: 412,
read: false
};
console.log(book.title); // "Dune"
Each key is a string (quotes optional when it is a valid identifier), followed by a colon and its value. Read or change a value with dot notation (book.title), and you can attach a property that did not exist yet by simply assigning to it:
book.author = "Frank Herbert";
book.read = true;
After this, book holds four properties; none had to be declared ahead of time. Asking for a key that is not there is not an error — book.publisher is simply undefined.
flowchart TD O["book object"] --> K1["title: Dune"] O --> K2["pages: 412"] O --> K3["read: false"] style O fill:#e0900b,color:#fff
Bracket notation and listing keys
Dot notation is the everyday choice, but it only works when the key is a literal, valid identifier. When the key lives in a variable, or contains spaces or characters an identifier cannot, use bracket notation:
const scores = { math: 90, science: 85, art: 78 };
const subject = "science";
console.log(scores[subject]); // 85
console.log(scores["art"]); // 78
Notice scores.subject would hunt for a property literally named subject and return undefined; brackets are how you say "use the value of this variable as the key". To see what an object holds, Object.keys lists its key names and Object.values lists the values. Both return arrays, which is why you can loop over them — and for a plain literal that means every key you wrote.
const scores = { math: 90, science: 85, art: 78 };
console.log(Object.keys(scores));
console.log(Object.values(scores));
let total = 0;
for (const subject of Object.keys(scores)) {
total += scores[subject]; // bracket access with a variable key
}
console.log(total);
flowchart TD
Q{"Key known and<br/>a valid identifier?"} -->|Yes| D["use dot: obj.key"]
Q -->|No, or dynamic| B["use brackets: obj key"]
style D fill:#1e7f3d,color:#fff
style B fill:#b45309,color:#fff
Nesting, mutation, and removal
Values inside an object are not stuck where you first put them. You reassign them at any time, and a value can itself be another object — which is how you model nested shapes like an order that contains an item. Reach a nested value by chaining dot accesses, walking one level at a time: order.item.name.
Removing a key uses the delete operator: delete order.item.price drops the price property entirely, rather than blanking it to undefined. Deletion is destructive, so reach for it deliberately — the immutable alternative is to build a fresh object with spread and leave the original untouched.
const user = { name: "Ada", stats: { score: 10, level: 1 } };
console.log(user.stats.score);
user.stats.score = 25;
console.log(user.stats.score);
delete user.stats.level;
console.log(Object.keys(user.stats));
flowchart LR A["const a = obj"] --> O["one object in memory"] B["const b = a"] --> O O -.->|"change via b<br/>is visible through a"| A style O fill:#b91c1c,color:#fff
Objects are shared, not copied
Here is the trap that bites every JavaScript learner. Assigning a number copies it — after let b = a, changing b leaves a alone. Objects do not behave that way. After const b = a where a is an object, b is not a second copy; it is a second name for the very same object in memory. Change a property through b and a sees the change too, because there was only ever one object.
This is why a function that mutates an object argument can quietly change its caller's original. To make a genuine, independent copy, spread into a new object: { ...a }. Then the two can drift apart without interfering.
Methods: functions as property values
Because a function is just a value, you can store one as a property. A function that lives on an object is called a method, and you call it with dot notation, exactly like reading any other value: counter.bump().
Inside a method, the keyword this refers to the object the method was called on, so this.count += 1 updates that object's own count. Methods are how objects gain behavior, not just data. A later lesson digs into this and the rules that make it subtle; for now, read this as "the object to the left of the dot".
const counter = {
count: 0,
bump() { this.count += 1; }
};
counter.bump();
counter.bump();
console.log(counter.count);
What does this print? We read one property, then list every key.
const user = { name: "Ada", age: 30 };
console.log(user.name);
console.log(Object.keys(user));
user.name is the string "Ada".
Object.keys returns an array of the key names.
Write totalAge(people) where people is an array of objects like {age: 10}. Return the sum of every person's age.
function totalAge(people) {
// return the sum of all ages
}
Start a running total at 0.
Loop with for...of and add each person.age.
function totalAge(people) {
let total = 0;
for (const person of people) {
total += person.age;
}
return total;
}
Write totalValues(obj) that returns the sum of every value in obj (assume each value is a number). Iterate with Object.values.
function totalValues(obj) {
// return the sum of all values
}
Object.values(obj) gives you an array of the values.
Loop over it with for...of and add each one to a running total.
function totalValues(obj) {
let total = 0;
for (const v of Object.values(obj)) {
total += v;
}
return total;
}
This tries to return a profile with a new city while leaving the caller's original alone. It fails: const copy = profile makes copy another name for the same object, so setting copy.city also changes the original profile. Make the copy independent with object spread.
function withNewCity(profile) {
const copy = profile;
copy.city = "Lagos";
return copy;
}
Assigning an object shares it; both names see the same data.
Build a real copy with { ...profile } before mutating.
function withNewCity(profile) {
const copy = { ...profile };
copy.city = "Lagos";
return copy;
}
What does this print? Reach a nested value through two dot accesses, then remove a key and list what remains.
const order = { id: 7, item: { name: "Book", price: 12 } };
console.log(order.item.name);
delete order.item.price;
console.log(Object.keys(order.item));
order.item.name walks two levels to the string "Book".
After delete, order.item has only the name key left.
Recap
- An object is a bundle of key/value pairs written in
{}; reach a value with dot (obj.key) or bracket (obj[key]) notation. - Reach for brackets when the key lives in a variable or is not a valid identifier; dot needs a literal name.
Object.keysandObject.valuesreturn arrays of the names and the values, ready to loop over.- Properties are mutable: assign to add or change,
deleteto remove, and chain dots to reach nested data. - Assigning an object shares it — both names see the same data, so a real copy needs spread.
Next you'll learn how arrays hold ordered lists of values and the methods that make them powerful.