Skip to content
academia.sh

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; 1 and "1" are the same key.
  • A field being absent and carrying undefined are separate situations; the distinction is seen with Object.hasOwn.
  • Object.entries and Object.fromEntries are 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.

Start typing to search.

↑↓ Esc navigate · open · close