Skip to main content

Objects (Plain Objects)

What are objects?

Objects are collections of key-value pairs. They let you group related data together and access values by name.

const user = {
name: "Alice",
age: 30,
isAdmin: false
};

console.log(user.name); // "Alice"
console.log(user.age); // 30

This guide covers plain objects, also called object literals. Classes and object-oriented programming are covered in the OOP guide.

Why this matters

Objects are how JavaScript represents structured data. You'll use them for users, settings, API responses, products, form values, and many other things that have named properties.

Objects are also the foundation for many JavaScript APIs. When you understand plain objects, arrays of objects, property access, and reference behavior, much of JavaScript becomes easier to read.

Creating objects

Use curly braces to create an object literal:

const empty = {};

const person = {
name: "Alice",
age: 30,
city: "Cleveland"
};

Each property has a key and a value:

const person = {
name: "Alice", // key: name, value: "Alice"
age: 30 // key: age, value: 30
};

Keys are usually strings, and values can be any JavaScript value: strings, numbers, booleans, arrays, other objects, or functions.

Accessing properties

Dot notation

Use dot notation when you know the property name and it is a valid identifier:

const person = {
name: "Alice",
age: 30
};

console.log(person.name); // "Alice"
console.log(person.age); // 30

Bracket notation

Use bracket notation when the property name is stored in a variable or is not a valid identifier:

const person = {
name: "Alice",
age: 30
};

const key = "name";

console.log(person[key]); // "Alice"

Bracket notation is required for property names with spaces or special characters:

const settings = {
"font size": 16,
"2faEnabled": true
};

console.log(settings["font size"]); // 16
console.log(settings["2faEnabled"]); // true

If a property does not exist, JavaScript returns undefined:

const person = {
name: "Alice"
};

console.log(person.city); // undefined

Property shorthand and computed keys

When a variable name matches the property name you want, you can use property shorthand:

const name = "Alice";
const age = 30;

const person = {
name,
age
};

console.log(person); // { name: "Alice", age: 30 }

Use computed property names when the key is stored in a variable:

const key = "theme";
const value = "dark";

const settings = {
[key]: value
};

console.log(settings); // { theme: "dark" }

Adding, changing, and removing properties

Objects are mutable. You can add, update, and remove properties after creating the object.

Adding properties

const person = {
name: "Alice"
};

person.age = 30;
person.city = "Cleveland";

console.log(person); // { name: "Alice", age: 30, city: "Cleveland" }

Changing properties

const person = {
name: "Alice",
age: 30
};

person.age = 31;

console.log(person.age); // 31

Removing properties

Use delete to remove a property:

const person = {
name: "Alice",
age: 30,
city: "Cleveland"
};

delete person.city;

console.log(person); // { name: "Alice", age: 30 }

Nested objects

Objects can contain other objects and arrays:

const user = {
name: "Alice",
address: {
city: "Cleveland",
zipCode: "10001"
},
roles: ["admin", "editor"]
};

console.log(user.address.city); // "Cleveland"
console.log(user.roles[0]); // "admin"

Nested data is common in API responses. Keep nesting reasonable; deeply nested objects can become hard to read and update.

Use optional chaining (?.) when a nested property might be missing:

const user = {
name: "Alice"
};

console.log(user.address?.city); // undefined

Without ?., user.address.city would cause an error because address is missing.

Objects are reference values

Objects are reference values. When you assign an object to another variable, both variables refer to the same object:

const first = { name: "Alice" };
const second = first;

second.name = "Bob";

console.log(first.name); // "Bob"
console.log(second.name); // "Bob"

This is the same reference behavior you saw with arrays. To make a shallow copy, use spread syntax:

const original = { name: "Alice", age: 30 };
const copy = { ...original };

copy.name = "Bob";

console.log(original.name); // "Alice"
console.log(copy.name); // "Bob"

This is a shallow copy. Nested objects are still shared.

const original = {
name: "Alice",
address: {
city: "Cleveland"
}
};

const copy = { ...original };

copy.address.city = "Boston";

console.log(original.address.city); // "Boston"

The top-level object was copied, but the nested address object was still shared.

Rest and spread syntax are covered in more detail in the rest and spread guide.

Looping over objects

There are several ways to loop over an object's properties.

Object.keys()

Use Object.keys() when you need property names:

const person = {
name: "Alice",
age: 30,
city: "Cleveland"
};

for (const key of Object.keys(person)) {
console.log(key, person[key]);
}

Output:

name Alice
age 30
city Cleveland

Object.values()

Use Object.values() when you only need values:

const person = {
name: "Alice",
age: 30,
city: "Cleveland"
};

for (const value of Object.values(person)) {
console.log(value);
}

Output:

Alice
30
Cleveland

Object.entries()

Use Object.entries() when you need both keys and values:

const person = {
name: "Alice",
age: 30,
city: "Cleveland"
};

for (const [key, value] of Object.entries(person)) {
console.log(`${key}: ${value}`);
}

Output:

name: Alice
age: 30
city: Cleveland

for...in

You may also see for...in:

const person = {
name: "Alice",
age: 30
};

for (const key in person) {
console.log(key, person[key]);
}

for...in can include inherited properties. For most beginner code, prefer Object.keys(), Object.values(), or Object.entries().

Checking properties

Use the in operator to check whether a property exists:

const person = {
name: "Alice",
age: 30
};

console.log("name" in person); // true
console.log("city" in person); // false

Use Object.hasOwn() when you specifically want an object's own property:

const person = {
name: "Alice"
};

console.log(Object.hasOwn(person, "name")); // true
console.log(Object.hasOwn(person, "city")); // false

If you only care whether a property value is not missing, check the value:

if (person.name !== undefined) {
console.log(person.name);
}

Default values

Use ?? when you want a default only for null or undefined:

const settings = {
retries: 0
};

const retries = settings.retries ?? 3;

console.log(retries); // 0

Use || when any falsy value should use the default:

const user = {
displayName: ""
};

const displayName = user.displayName || "Guest";

console.log(displayName); // "Guest"

Rule of thumb: Use ?? when 0, false, or "" should remain valid values.

Copying and merging objects

Use spread syntax to copy or merge objects:

const defaults = {
theme: "light",
language: "en"
};

const userPrefs = {
theme: "dark"
};

const settings = {
...defaults,
...userPrefs
};

console.log(settings); // { theme: "dark", language: "en" }

Later properties override earlier properties when keys are the same.

For more copying, merging, and destructuring patterns, see the rest and spread guide.

Destructuring

Destructuring lets you pull properties into variables:

const person = {
name: "Alice",
age: 30
};

const { name, age } = person;

console.log(name); // "Alice"
console.log(age); // 30

You can rename a property while destructuring:

const { name: userName } = person;

console.log(userName); // "Alice"

You can also provide a default value while destructuring:

const person = {
name: "Alice"
};

const { name, role = "member" } = person;

console.log(name); // "Alice"
console.log(role); // "member"

Objects vs arrays

Use objects when:

  • You need named properties
  • The data represents one structured thing
  • You need to look up values by key

Use arrays when:

  • Order matters
  • You have a list of similar items
  • You want to process every item with array methods

Objects and arrays often work together:

const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" }
];

const names = users.map((user) => user.name);

console.log(names); // ["Alice", "Bob"]

Common patterns

Building an object incrementally

const scores = {};

scores.alice = 95;
scores.bob = 87;

console.log(scores); // { alice: 95, bob: 87 }

Counting values

const words = ["apple", "banana", "apple"];
const counts = {};

for (const word of words) {
counts[word] = (counts[word] ?? 0) + 1;
}

console.log(counts); // { apple: 2, banana: 1 }
const config = {
apiUrl: "https://api.example.com",
timeout: 5000,
retries: 3
};

Best practices

  • Use object literals: {} is the clearest way to create plain objects.
  • Use dot notation when possible: It is shorter and easier to read.
  • Use bracket notation for dynamic keys: Use it when the property name is stored in a variable.
  • Use optional chaining for uncertain nested data: user.address?.city avoids errors when a middle property is missing.
  • Prefer Object.entries() for key-value loops: It makes both pieces visible.
  • Use ?? for safe defaults: It preserves valid falsy values like 0 and "".
  • Remember reference behavior: Copy objects before changing them if the original should stay unchanged.
  • Remember that spread is shallow: Nested objects are still shared unless you copy them too.
  • Avoid deep nesting when possible: Deep data is harder to update safely.

Summary

Objects group related values under meaningful property names. Use dot notation for known property names, bracket notation for dynamic names, and Object.keys(), Object.values(), or Object.entries() when looping. Objects are reference values, so copying and mutation matter. When your data has named properties, an object is usually the right tool.