Objects
Bags of named values — how to create them, read them, change them, and compose them into real-world data models.
If an array is an ordered list of values, an object is an unordered bag of named values. Objects are how JavaScript models "things with properties": a user with a name and an email, a configuration with a host and a port, a product with a price and a title.
Objects are the most important compound type in JavaScript. The language itself is built around them.
Creating objects
The classic syntax — object literal notation:
Each entry is a property with a key (the name) and a value. Keys are strings (with very rare exceptions you can ignore for now). Values can be anything — numbers, strings, booleans, arrays, other objects, even functions.
Reading properties: dot vs bracket notation
There are two ways to read a property:
- Dot notation:
user.name. Concise; preferred when the key is a valid identifier you know at write-time. - Bracket notation:
user["name"]. Required when the key is not a valid identifier (has spaces, starts with a digit) or when it is computed at runtime.
That last line is important: reading a property that doesn't
exist returns undefined, not an error. That is convenient and a
common source of subtle bugs.
Writing properties
You can set (or change, or add) a property with assignment:
Notice that const user = {...} does not prevent us from
modifying the object. const only prevents reassigning user
to point at a different object. The object itself is mutable.
Shorthand property names
When a variable already holds the value you want to store under the same name, you can omit the repetition:
This crops up all the time in modern JavaScript. Functions returning objects often use the shorthand to keep things tidy:
function makeUser(name, age) {
return { name, age };
}Methods: functions inside objects
When a property's value is a function, we call it a method. There is a tidy shorthand for declaring methods:
Notice the keyword this. Inside a method, this refers to the
object that the method was called on. We'll touch on this more
in the next chapter; for now, the rule is "this inside a method
is the object before the dot".
Iterating over objects
Three common ways to walk an object's contents.
For most everyday code, Object.entries + for...of is the
clearest pattern.
Destructuring objects
Just like with arrays, we can pull properties out by name in one line. This is one of modern JavaScript's most-loved features.
Destructuring is wonderful in function parameters, where it
replaces a stack of options.x lookups:
Spread, rest, and copying
The ... syntax works for objects too.
A note: this is a shallow copy. If a property's value is itself an object or array, the reference is copied — both the original and the copy will share that nested object. We'll revisit this in the nested data chapter.
Objects are references, just like arrays
The same reference-vs-value point we made for arrays applies to all objects.
Comparing objects
Object equality with === is reference equality, not
structural equality. Two objects with identical contents are not
considered equal unless they are literally the same object.
The JSON trick has limitations (it can't handle functions, dates, circular structures, etc.), but for "plain data" it is a quick deep-equal check.
Why we use objects
Objects let you give meaning to a group of related values without inventing a custom data type. Compare:
// Loosely-grouped values — easy to mix up
function move(playerName, x, y, hp, isAlive) { ... }
// Grouped in an object — meaning is obvious, hard to mis-order
function move(player) { ... }
const player = { name: "Ada", x: 10, y: 20, hp: 100, isAlive: true };The second style scales as your domain grows. Adding a new
property (say, score) is a one-line change; no function
signatures break.
A multi-file object example
A typical pattern: one file defines functions that take and return objects representing some piece of your domain; another file uses them.
Notice that each function takes a player object and returns a
new player object instead of mutating the old one. The driver
then reassigns p to the new value. This is a very common
modern style — sometimes called immutable updates — and it
keeps the state of the program much easier to reason about.
Challenge
Write a function mergeUser(base, updates) that returns a new user object combining base and updates. Rules:
- Any property in
updatesoverrides the same property inbase. - Properties whose value in
updatesisundefinedshould be ignored (do not overwritebase). - The original
baseandupdatesmust not be mutated.
Examples:
mergeUser({name:"Ada",age:28}, {age:29})→{name:"Ada", age:29}mergeUser({name:"Ada",age:28}, {age:undefined, role:"admin"})→{name:"Ada", age:28, role:"admin"}
What does { ...base, ...updates } do in modern JavaScript?
It creates a deep clone of base and updates
It throws an error because objects can't be spread
It creates a new object combining the properties of base and updates. Where both objects have the same key, the later one (updates) wins.
It modifies base in place by adding the properties of updates