While arrays group values by position (first, second, third…), objects group values by name. They’re how JavaScript represents “a thing with attributes” — a user with a name and email, a product with a price and SKU, a configuration with a dozen named settings.
You build an object with curly braces, listing key: value pairs separated
by commas:
const user = {
name: "Ada Lovelace",
role: "admin",
age: 28,
};
user.name; // "Ada Lovelace" — dot access, the most common form
user["role"]; // "admin" — bracket access, for dynamic or unusual keys
Breaking down the syntax:
{ } — opens and closes the object.name — the key (also called a property name).: — separates the key from its value."Ada Lovelace" — the value stored under that key., — separates one key/value pair from the next.Use dot access (user.name) whenever you know the property name ahead of
time — it’s shorter and easier to read. Reach for bracket access
(user["role"]) when the key is stored in a variable or contains characters
that aren’t valid in an identifier:
const key = "role";
user[key]; // "admin" — looks up whatever property name `key` holds
Objects created with const can still have their properties changed — only
the binding (the variable name pointing to the object) is locked, not the
object’s contents:
const user = { name: "Ada", role: "admin" };
user.role = "owner"; // updates an existing property
user.email = "ada@example.com"; // adds a brand new property
console.log(user); // { name: "Ada", role: "owner", email: "ada@example.com" }
A property whose value is a function is called a method. Methods let an object describe behavior alongside its data:
const counter = {
count: 0,
increment() {
this.count += 1;
},
};
counter.increment();
counter.increment();
console.log(counter.count); // 2
Inside a method, this refers to the object the method was called on — here,
counter.
When a variable’s name matches the property name you want, you can skip
repeating it — and you can define methods without the function keyword:
const name = "Ada";
const role = "admin";
const user = { name, role }; // same as { name: name, role: role }
const counter = {
count: 0,
increment() { this.count += 1; }, // shorthand for increment: function () {...}
};
Just like arrays, objects can be destructured — but by name instead of position, which is the more common form you’ll see:
const user = { name: "Ada", role: "admin", age: 28 };
const { name, role } = user;
console.log(name); // "Ada"
console.log(role); // "admin"
const { name: displayName } = user; // rename while destructuring
console.log(displayName); // "Ada"
The spread syntax (...) copies an object’s properties into a new one —
handy for making updated copies without changing the original:
const user = { name: "Ada", role: "admin" };
const updated = { ...user, role: "owner" };
// { name: "Ada", role: "owner" } — a new object; `user` is untouched
When you need to inspect all of an object’s properties, Object.keys,
Object.values, and Object.entries are the modern way to do it:
const user = { name: "Ada", role: "admin" };
Object.keys(user); // ["name", "role"]
Object.values(user); // ["Ada", "admin"]
Object.entries(user); // [["name", "Ada"], ["role", "admin"]]
for (const [key, value] of Object.entries(user)) {
console.log(`${key}: ${value}`);
}
Before Object.keys/values/entries were available, looping over an
object’s properties meant reaching for for...in:
var user = { name: "Ada", role: "admin" };
for (var key in user) {
console.log(key + ": " + user[key]);
}
for...in still works, but it has a sharp edge: it also walks up an
object’s prototype chain, potentially visiting inherited properties you
didn’t intend to include — a frequent source of subtle bugs. Object.keys
(and friends) only look at the object’s own properties, which is what you
almost always want, so they’ve replaced for...in as the standard approach.
You may also encounter older code building objects with constructor
functions and new instead of plain object literals:
function User(name, role) {
this.name = name;
this.role = role;
}
var ada = new User("Ada", "admin");
This pattern still appears in legacy code and is the ancestor of modern
class syntax, but for grouping simple, related data together, a plain
object literal — { name: "Ada", role: "admin" } — remains the simplest and
most common choice.