Lesson 18 / 21
Objects
The two forms of property access, key stringification, shorthand and computed syntax, existence checks, destructuring, spread, and reference semantics.
Contents
The previous lesson used measurement records as objects, but the syntax was never defined. This lesson takes up objects: creation, access, separating, and combining.
An object is an ordered set of key–value pairs. Keys are strings or symbols; values can be of any type. The prototype chain, property descriptors, and class syntax are the next course’s subject; this lesson covers plain data objects.
Creation and Access
const field = "temperature"; const station = "A1"; const temperature = 21.5; const record = { station, temperature, [field + "Unit"]: "C", summary() { return `${this.station}: ${this.temperature}`; }, }; console.log(record); console.log(record.station, record["temperature"], record[field]); console.log(record.summary());
{
station: 'A1',
temperature: 21.5,
temperatureUnit: 'C',
summary: [Function: summary]
}
A1 21.5 21.5
A1: 21.5
Three writing shortcuts were used together. Shorthand property (station) takes its
value from a variable of the same name. Computed key ([field + "Unit"]) produces
the key at runtime. Shorthand method (summary()) writes a function property
without the function keyword; by the rule from the Arrow Functions lesson, methods are
not written in arrow form.
There are two forms of access. Dot notation requires a fixed, valid name. Bracket notation accepts any expression; it is required when the key is determined at runtime. In the output’s second line, all three access forms gave the same value.
The object being written across multiple lines is the runtime’s inspection format; once
an object passes a certain length it is split across lines, and function properties are
shown as [Function: name]. This display is not defined in the standard.
Keys Are Strings
Every key except a symbol is converted to a string:
const obj = {}; obj[1] = "number one"; obj["1"] = "text one"; obj[true] = "boolean"; console.log(obj); console.log(Object.keys(obj)); console.log(typeof Object.keys(obj)[0]);
{ '1': 'text one', true: 'boolean' }
[ '1', 'true' ]
string
1 and "1" are the same key; the second assignment overwrote the first. This is why
the indices that come out of for...in in the Loops lesson are strings — an array is
also an object and carries its indices as keys.
If a mapping with numeric keys is needed, the Map structure preserves the distinction;
that is outside this course’s scope.
Existence Checks
A field being absent and a field carrying the value undefined are different
situations:
const record = { station: "A1", humidity: undefined }; console.log(record.humidity, "humidity" in record, Object.hasOwn(record, "humidity")); console.log(record.pressure, "pressure" in record, Object.hasOwn(record, "pressure")); delete record.humidity; console.log("after delete:", "humidity" in record, record);
undefined true true
undefined false false
after delete: false { station: 'A1' }
Reading gave undefined in both cases; only the existence check showed the difference.
The in operator also counts inherited properties; Object.hasOwn looks only at the
object’s own properties. This course uses the latter.
delete removes the property entirely. Assigning undefined leaves the property in
place and only changes its value.
Key–Value Conversions
Moving back and forth between an object and an array is the basis of writing transformations:
const record = { station: "A1", temperature: 21.5, humidity: 48 }; console.log(Object.keys(record)); console.log(Object.values(record)); console.log(Object.entries(record)); const pairs = [["station", "B1"], ["temperature", 23]]; console.log(Object.fromEntries(pairs));
[ 'station', 'temperature', 'humidity' ]
[ 'A1', 21.5, 48 ]
[ [ 'station', 'A1' ], [ 'temperature', 21.5 ], [ 'humidity', 48 ] ]
{ station: 'B1', temperature: 23 }
Object.entries and Object.fromEntries are each other’s inverse. This pair makes it
possible to use array methods on an object: the object is converted to an array,
filtered or mapped, then converted back to an object.
Destructuring
Destructuring separates an object’s or array’s fields into separate names:
const record = { station: "A1", temperature: 21.5, location: { elevation: 120 } }; const { station, temperature: degrees, humidity = null } = record; console.log(station, degrees, humidity); const { location: { elevation } } = record; console.log(elevation); const { station: name, ...rest } = record; console.log(name, rest); const [first, , third = 0] = [21.5, 19.75]; console.log(first, third);
A1 21.5 null
120
A1 { temperature: 21.5, location: { elevation: 120 } }
21.5 0
Four abilities were used: renaming (temperature: degrees), default
(humidity = null), nested destructuring, and gathering the remaining fields.
The default’s criterion is the same as in the Parameters lesson: it engages only if the
value is undefined.
In array destructuring, order is by position; an element to skip is left blank. This is
the [index, value] syntax used with entries in the Loops lesson.
Spread and Combining
Spread syntax copies an object’s own enumerable properties into another object literal:
const base = { station: "A1", temperature: 21.5, unit: "C" }; const update = { temperature: 22.75 }; console.log({ ...base, ...update }); console.log({ ...update, ...base }); console.log(base);
{ station: 'A1', temperature: 22.75, unit: 'C' }
{ temperature: 21.5, station: 'A1', unit: 'C' }
{ station: 'A1', temperature: 21.5, unit: 'C' }
Order determines the result: a property with the same name written later overwrites the earlier one. In the second line, the order is reversed, so the update is lost. The third line shows the original object did not change — spread produces a new object.
The copy is shallow:
const deep = { station: "A1", location: { elevation: 120 } }; const copy = { ...deep }; copy.location.elevation = 999; console.log(deep.location.elevation);
999
The inner object was not copied; the two objects share the same location object. Deep
copying is outside this course’s scope and will be taken up in the Immutability
Techniques lesson.
Reference Semantics
Objects are bound to names by reference. The rule established in the Passing by Value and by Reference lesson of the Programming Fundamentals course applies here exactly:
const record = { station: "A1", temperature: 21.5 }; const alias = record; alias.temperature = 22.75; console.log(record.temperature); function mutates(r) { r.temperature = 0; } function doesNotMutate(r) { return { ...r, temperature: 0 }; } mutates(record); console.log(record.temperature); console.log(doesNotMutate(record), record.temperature);
22.75
0
{ station: 'A1', temperature: 0 } 0
The mutates function changed the caller’s data. doesNotMutate produced a new object
and left the original record as it was — the 0 in the last line is left over from the
mutates call.
This course’s rule: transformation functions do not change their inputs; they return a new object. If a side-effecting update is actually needed, the function’s name should say so.
Applying It to the Measurement Script
Grouping records by station is the object used as a map:
const records = [ { station: "A1", temperature: 21.5 }, { station: "A2", temperature: 19.75 }, { station: "A1", temperature: 22.75 }, { station: "B1", temperature: 23 }, ]; const groups = {}; for (const record of records) { const key = record.station; if (!Object.hasOwn(groups, key)) { groups[key] = []; } groups[key].push(record.temperature); } console.log(groups); const averages = Object.fromEntries( Object.entries(groups).map(([name, values]) => [ name, values.reduce((t, v) => t + v, 0) / values.length, ]), ); console.log(averages);
{ A1: [ 21.5, 22.75 ], A2: [ 19.75 ], B1: [ 23 ] }
{ A1: 22.125, A2: 19.75, B1: 23 }
Both steps use this lesson’s tools. Grouping writes to the object with a computed key
and does the existence check with Object.hasOwn. The average computation converts the
object to pairs, applies a map, and converts it back to an object. The callback’s
parameter was destructured with the [name, values] syntax.
Summary
- Shorthand property, computed key, and shorthand method are the three shortcuts of object syntax.
- Keys other than symbols are converted to strings;
1and"1"are the same key. - A field being absent and carrying
undefinedare separate situations; the distinction is seen withObject.hasOwn. Object.entriesandObject.fromEntriesare each other’s inverse and make it possible to use array methods on an object.- Destructuring offers renaming, defaults, nested destructuring, and gathering remaining fields.
- In spread, a property written later overwrites the earlier same-named one; the copy is shallow, and objects are bound to names by reference.
Next Step
Up to this lesson, output was produced with console.log arguments or concatenation.
The next lesson takes up strings: formatting with template literals, search and split
methods, the practical consequences of the code-unit/grapheme distinction, and number
formatting. The measurement report’s readable text will be written there.
To keep your progress and take notes, Log in
My notes
Log in to take notes.